suppressWarnings(library(Seurat))
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
suppressWarnings(library(scran))
suppressWarnings(library(scDblFinder))
suppressWarnings(library(tidyverse))
suppressWarnings(library(RColorBrewer))
suppressWarnings(library(openxlsx))
suppressWarnings(library(circlize))
suppressWarnings(library(gtools))
suppressWarnings(library(ggExtra))
suppressWarnings(library(ggridges))
suppressWarnings(library(patchwork))

projectDir <- "."
projectPath <- file.path(projectDir)
outputPath <- file.path(projectPath)
dataDir <- "data"
dataPath <- file.path(projectPath,dataDir)
prefix <- "Myeloid"

dir.create(file.path(outputPath),recursive = T)

setwd(outputPath)

color.list <- c("#ebac23", "#b80058", "#008cf9",
                "#006e00", "#00bbad", "#d163e6",
                "#b24502", "#ff9287", "#5954d6",
                "#00c6f8", "#878500", "#00a76c",
                "#bdbdbd", "#846b54",
                brewer.pal(12, "Paired"),
                brewer.pal(12, "Set3"),
                brewer.pal(8, "Pastel2"),
                colorRampPalette(c("grey20","grey70"))(4))

Load Counts

sc <- Read10X(file.path(dataPath,"myeloid.counts"))
sc <- CreateSeuratObject(
  counts = sc,
  assay = "RNA",
  project = "Myeloid",
  names.field = c(1,2),
  names.delim = "_",
  min.cells = 0,
  min.features = 0
)
sc@meta.data$SampleName <- sc@meta.data$orig.ident

table(sc$SampleName)

CD11b_KO CD11b_WT 
    8931    11395 

Integration by Sample using CCA

cellSet <- "Myeloid"
subsetName <- "Integrated"
nfeatures <- 2000
ress <- c(0.5)
npcs <- 20

bySample <- SplitObject(sc, split.by = "SampleName")

# Integration via CCA protocol
bySample <- lapply(bySample, function (x) {
  x <- x %>% NormalizeData(verbose=F) %>%
    FindVariableFeatures(nfeatures = nfeatures,
                         verbose=F) %>%
    ScaleData(verbose=F) %>%
    RunPCA(verbose=F)
  return(x)
})

commonFeatures <- SelectIntegrationFeatures(bySample,
                                            verbose=F)

smanchors <- FindIntegrationAnchors(bySample,
                                    anchor.features = commonFeatures,
                                    verbose=F)

  |                                                  | 0 % ~calculating  
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=06m 18s
bySample.cca <- IntegrateData(anchorset = smanchors,
                              features.to.integrate = rownames(bySample[[1]]),
                              verbose=F)
DefaultAssay(bySample.cca) <- "integrated"

# Run the standard workflow for clustering and visualization of integrated data
bySample.cca <- bySample.cca %>%
  ScaleData(verbose = FALSE) %>%
  RunPCA(verbose = FALSE) %>%
  FindNeighbors(reduction = "pca",
                dims = seq(npcs),
                force.recalc = T,
                verbose=F) %>%
  FindClusters(resolution = ress,
               verbose=F) %>%
  RunUMAP(reduction = "pca",
          dims = seq(npcs),
          verbose=F) %>%
  RunTSNE(reduction = "pca",
          dims = seq(npcs),
          perplexity=100,
          verbose=F)
Warning: The default method for RunUMAP has changed from calling Python UMAP via reticulate to the R-native UWOT using the cosine metric
To use Python UMAP via reticulate, set umap.method to 'umap-learn' and metric to 'correlation'
This message will be shown once per session
# Reannotate clusters
bySample.cca@meta.data <- bySample.cca@meta.data %>%
  mutate(across(starts_with("integrated_"),.fns = function (x){paste0("C",x)})) %>%
  mutate(across(starts_with("integrated_"),.fns = function (x){factor(x)})) %>%
  mutate(across(starts_with("integrated_"),.fns = function (x){factor(x,levels=mixedsort(levels(x)))}))

saveRDS(bySample.cca,file = file.path(outputPath,paste(cellSet,subsetName,"seurat","rds",sep = ".")))

bySample.cca
An object of class Seurat 
55996 features across 20326 samples within 2 assays 
Active assay: integrated (27998 features, 2000 variable features)
 1 other assay present: RNA
 3 dimensional reductions calculated: pca, umap, tsne
clustering <- "integrated_snn_res.0.5"
p1 <- DimPlot(bySample.cca,
              reduction = "tsne",
              group.by = "SampleName",
              cols = color.list)
p2 <- DimPlot(bySample.cca,
              reduction = "tsne",
              group.by = clustering,
              cols = color.list,
              label = T)
p1 + p2

DimPlot(bySample.cca,
        reduction = "tsne",
        group.by = clustering,
        cols = color.list,
        split.by = "SampleName")

Cell Identification based in SingleR framework

Clusters and Cells are classified based on SingleR method using Blueprint Encode, the Human Primary Cell Atlas and the Monaco Immune cell type profiles collection. This identification is not precise and should be interpreted with caution.

library(SingleR)
useAssay <- "RNA"

clustering <- "integrated_snn_res.0.5"
Idents(bySample.cca) <- clustering

bySample.cca.WT <- subset(bySample.cca,SampleName == "CD11b_WT")

nCores <- 1

immGen=celldex::ImmGenData()
snapshotDate(): 2021-05-18
see ?celldex and browseVignettes('celldex') for documentation
loading from cache
see ?celldex and browseVignettes('celldex') for documentation
loading from cache
singler <- list(
  byCluster = list()
)

singler <- SingleR(
    clusters = Idents(bySample.cca.WT)
    , test = Seurat::Assays(bySample.cca.WT,slot = useAssay)@data
    , ref = immGen
    , labels = immGen$label.fine
    , genes = "de"
    , quantile = 0.8
    , fine.tune = T
    , tune.thresh = 0.05
    , sd.thresh = 1
  )

bySample.cca@meta.data <- bySample.cca@meta.data %>%
  mutate(immGenLabels = singler[integrated_snn_res.0.5,"first.labels"],
         immGenFTLabels = singler[integrated_snn_res.0.5,"labels"])

saveRDS(bySample.cca,file = file.path(outputPath,paste(cellSet,subsetName,"seurat","rds",sep=".")))  
p1 <- DimPlot(bySample.cca,
              reduction = "tsne",
              group.by = "immGenFTLabels",
              cols = color.list)
p2 <- DimPlot(bySample.cca,
              reduction = "tsne",
              group.by = clustering,
              cols = color.list,
              label = T)
p1 + p2

df <- Embeddings(bySample.cca,reduction = "tsne")
df <- cbind(df,bySample.cca@meta.data)
df <- df %>%
  mutate(ManualClustering = factor(ifelse(
    tSNE_1 > -25.5 & integrated_snn_res.0.5 == "C9" ,"C9b",as.character(integrated_snn_res.0.5)
    ))) %>%
  mutate(ManualClustering = factor(ManualClustering,levels=mixedsort(levels(ManualClustering))))
bySample.cca@meta.data$ManualClustering <- df$ManualClustering
DimPlot(bySample.cca,
        reduction = "tsne",
        group.by = "ManualClustering",
        cols = color.list,label = T,pt.size = 2)

Get Monocyte Modules from Krenkel et al publication

Data obtained from Krenkel et. al. Gut 2020. Dataset GSE131834. Clusterized using standard procedures (2000 variable features and 20 PCA components for clustering and dimensionality reduction).

Final annotations to extract cell type signatures imported from original analysis.

krenelMetadata <- read.delim(
  file = file.path(dataPath,"GSE131834_annotation_bonemarrow.csv"),
  sep=",", header = T)

colnames(krenelMetadata) <- c("cellId", "cell_type")

krenelMetadata <- krenelMetadata %>%
  mutate(cellId=sub("-",".",cellId))
rownames(krenelMetadata) <- krenelMetadata$cellId

krenkelCounts <- read.table(
  file = file.path(dataPath,"GSE131834_BM_ND_WD_WT16.txt"),
  sep = "\t",header=T)
  
krenkelCounts <- Matrix::Matrix(as.matrix(krenkelCounts))

krenkelSeurat <- CreateSeuratObject(
    krenkelCounts[,rownames(krenelMetadata)],
    project = "Krenkel et al",
    assay = "RNA",
    meta.data = krenelMetadata
  )
  
nfeatures = 2000
npcs <- 20
ress <- c(0.5)
krenkelSeurat <- krenkelSeurat %>%
  NormalizeData(
    verbose = F
  ) %>%
  FindVariableFeatures(
    nfeatures=nfeatures,
    verbose = F
  ) %>%
  ScaleData(
    verbose = F
  ) %>%
  RunPCA(
    verbose = F
  ) %>%
  FindNeighbors(
    reduction = "pca",
    dims = seq(npcs),
    force.recalc=T,
    verbose = F,
  ) %>%
  FindClusters(
    resolution=ress,
    verbose = F
  ) %>%
  RunUMAP(
    reduction = "pca",
    dims = seq(npcs),
    verbose = F
  ) %>%
  RunTSNE(
    reduction = "pca",
    dims = seq(npcs),
    perplexity = 100,
    verbose = F
  )
clustering <- "RNA_snn_res.0.5"
Idents(krenkelSeurat) <- clustering
p1 <- DimPlot(krenkelSeurat,
              reduction = "tsne",
              group.by = "RNA_snn_res.0.5",
              cols = color.list,
              label = T)
p2 <- DimPlot(krenkelSeurat, 
              reduction = "tsne", 
              group.by = "cell_type",
              cols = color.list,
              label = T)
p1+p2

Get Krenkel Cell Type Markers

Idents(krenkelSeurat) <- "cell_type"
markersKrenkel <- krenkelSeurat %>%
  FindAllMarkers(
    assay = "RNA",
    test.use = "wilcox",
    slot = "data",
    only.pos = T,
    verbose = F)
table(markersKrenkel[markersKrenkel$p_val_adj < 0.01,"cluster"])

  5 CMoP II       9 HSC   6 preDC I     1 BMM I    2 BMM II 
        468         772         716         196         366 
  3 BMM III    4 CMoP I  7 preDC II 8 preDC III 
        267         367         210         762 

HeatMap of Top 100 Krenkel Cell Type Markers

top100 <- markersKrenkel %>%
  filter(p_val_adj < 0.01) %>%
    group_by(cluster) %>%
    top_n(n = 100, wt = avg_log2FC)
krenkelSeurat <- ScaleData(krenkelSeurat,
                    features = unique(sort(c(VariableFeatures(krenkelSeurat),top100$gene))),
                    verbose = F)
DoHeatmap(krenkelSeurat, features = top100$gene) + NoLegend()

Top 20 Krenkel Markers DotPlot

ntop <- 10
pval <- 0.01

topMarkers <- markersKrenkel %>%
  filter(p_val_adj < pval) %>%
  group_by(cluster) %>%
  top_n(ntop, avg_log2FC) %>%
  ungroup() %>%
  dplyr::select(gene) %>%
  distinct()

DotPlot(
  krenkelSeurat,
  features = rev(topMarkers)
  ) +
  theme(axis.text.x = element_text(angle = 45,hjust = 1))

Select Cell Type Module genes from markers

Gene modules selected from the top 200 marker genes at an adjusted pval < 0.01, a mínimum logFC of 0.25, present in our Stromal dataset and not shared between cell types.

celltypeMarkers <- markersKrenkel %>%
  filter(p_val_adj < 0.01) %>%
  filter(avg_log2FC > 0.25) %>%
  filter(gene %in% rownames(bySample.cca)) %>%
  group_by(cluster) %>%
  top_n(n = 200, wt = avg_log2FC) %>%
  filter(gene %in% rownames(bySample.cca)) %>%
  dplyr::select(cluster,gene)

whichDup <- unique(sort(celltypeMarkers$gene[which(duplicated(celltypeMarkers$gene))]))
celltypeMarkers <- celltypeMarkers %>% filter(!gene %in% whichDup)

table(celltypeMarkers$cluster)

  5 CMoP II       9 HSC   6 preDC I     1 BMM I    2 BMM II 
        112         137         122          86         101 
  3 BMM III    4 CMoP I  7 preDC II 8 preDC III 
         93          41         102          16 
krenkelSeurat <- ScaleData(krenkelSeurat,
                    features = unique(sort(c(VariableFeatures(krenkelSeurat),celltypeMarkers$gene))),
                    verbose = F)
DoHeatmap(krenkelSeurat, features = celltypeMarkers$gene) + NoLegend()

moduleGeneListKrenkel <- celltypeMarkers %>%
  group_by(cluster) %>%
  group_split()
moduleGeneListKrenkel <- as.list(moduleGeneListKrenkel)
names(moduleGeneListKrenkel) <- levels(factor(celltypeMarkers$cluster))

Get Neutrophil Modules from Xie et al publication

Data obtained from Xie et. al. Nature 2020. Dataset GSE137539.

Clustering of neutrophils from wt neutrophil samples. Clustering of samples using CCA for integration and batch effect corrections (2000 variable features, 20 PCA components).

Final annotations to extract cell type signatures imported from original analysis.

xieMetadata <- read.delim(
  file = file.path(dataPath,"GSE137539_wt_ctl_meta.txt"),
  sep="\t", header = T)
xieMetadata <- xieMetadata %>%
  mutate(cellId = rownames(.)) %>%
  dplyr::select(cellId, orig.ident, cell_type, cluster)

myCountsFiles <- dir(file.path(dataPath),pattern = "GSM.+wt_ctl")
xieCounts <- lapply(myCountsFiles, function (f,dataPath) {
  sname <- gsub("_",".",gsub("GSM\\d+_(wt_ctl_\\w+).+","\\1",f))
  counts <- read.table(
    file = gzfile(file.path(dataPath,f),open = "r"),
    sep = " ",header=T)
    colnames(counts) <- paste0(sname,"_",colnames(counts))
    counts <- Matrix::Matrix(as.matrix(counts))
},dataPath) 

xieCounts <- do.call(cbind,xieCounts)
xieCounts <- xieCounts[,rownames(xieMetadata)]
xieSeurat <- CreateSeuratObject(
    counts = xieCounts,
    project = "Xie et al",
    assay = "RNA",
    meta.data = xieMetadata,
    names.delim = "_",
    names.field = 1
  )
xieSeurat <- xieSeurat[,!is.na(xieSeurat$cluster)]

xieSeuratList <- SplitObject(xieSeurat,split.by = "orig.ident")
  
xieSeuratList <- lapply(xieSeuratList, function (x) {
    x <- x %>% NormalizeData(
      verbose = F
    ) %>%
      FindVariableFeatures(
        nfeatures = nfeatures,
        verbose = F
        ) %>%
      ScaleData(
        verbose = F
      ) %>%
      RunPCA(
        verbose = F
      )
    return(x)
  })
  
commonFeatures <- SelectIntegrationFeatures(xieSeuratList,
                                            verbose = F)
  
smanchors <- FindIntegrationAnchors(
  xieSeuratList,
  anchor.features = commonFeatures,
  verbose = F
  )

  |                                                  | 0 % ~calculating  
  |+++++++++                                         | 17% ~03m 37s      
  |+++++++++++++++++                                 | 33% ~03m 22s      
  |+++++++++++++++++++++++++                         | 50% ~03m 24s      
  |++++++++++++++++++++++++++++++++++                | 67% ~01m 54s      
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~54s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=05m 26s
  
xieSeurat.cca <- IntegrateData(anchorset = smanchors,
                               verbose = F)
  
DefaultAssay(xieSeurat.cca) <- "integrated"
  
# Run the standard workflow for visualization and clustering
nfeatures <- 2000
npcs <- 20
xieSeurat.cca <- xieSeurat.cca %>%
  ScaleData(
    verbose = F
  ) %>%
  RunPCA(
    npcs = 50,
    verbose = F
  ) %>%
  FindNeighbors(
    reduction = "pca",
    dims = seq(npcs),
    force.recalc = T,
    verbose = F
  ) %>%
  FindClusters(
    resolution = ress,
    verbose = F
  ) %>%
  RunUMAP(
    reduction = "pca",
    dims = seq(npcs),
    verbose = F
  ) %>%
  RunTSNE(
    reduction = "pca",
    dims = seq(npcs),
    perplexity=100,
    verbose = F
  )
clustering <- "integrated_snn_res.0.5"
Idents(xieSeurat.cca) <- clustering
p1 <- DimPlot(xieSeurat.cca,
              reduction = "tsne",
              group.by = clustering,
              cols = color.list,
              label = T)
p2 <- DimPlot(xieSeurat.cca, 
              reduction = "tsne", 
              group.by = "cell_type",
              cols = color.list,
              label = T)
p1+p2

p1 <- DimPlot(xieSeurat.cca,
              reduction = "tsne",
              group.by = clustering,
              cols = color.list,
              label = T)
p2 <- DimPlot(xieSeurat.cca, 
              reduction = "tsne", 
              group.by = "cluster",
              cols = color.list,
              label = T)
p1+p2

Subset Xie to Bone Marrow derived Neutrophils

Select samples from bone marrow and remove those left at clusters G5 which clearly comes from SP or PB.

xieSeurat.cca <- subset(xieSeurat.cca, orig.ident %in% c("wt.ctl.bm1","wt.ctl.bm2") & cluster %in% c("G0","G1","G2","G3","G4","GM"))
p1 <- DimPlot(xieSeurat.cca,
              reduction = "tsne",
              group.by = clustering,
              cols = color.list,
              label = T)
p2 <- DimPlot(xieSeurat.cca, 
              reduction = "tsne", 
              group.by = "cluster",
              cols = color.list,
              label = T)
p1+p2

Get Xie Cell Type Markers

DefaultAssay(xieSeurat.cca) <- "RNA"
Idents(xieSeurat.cca) <- "cluster"
markersXie <- xieSeurat.cca %>%
  FindAllMarkers(
    assay = "RNA",
    test.use = "wilcox",
    slot = "data",
    only.pos = T,
    verbose = F)
table(markersXie[markersXie$p_val_adj < 0.01,"cluster"])

  G0   G4   G2   G1   G3   GM 
2943  423  621 2226  153   50 

HeatMap of Top 100 Krenkel Cell Type Markers

top100 <- markersXie %>%
  filter(p_val_adj < 0.01) %>%
    group_by(cluster) %>%
    top_n(n = 100, wt = avg_log2FC)
xieSeurat.cca <- ScaleData(xieSeurat.cca,
                    features = unique(sort(c(VariableFeatures(xieSeurat.cca),top100$gene))),
                    verbose = F)
DoHeatmap(xieSeurat.cca, features = top100$gene) + NoLegend()

Top 20 Krenkel Markers DotPlot

ntop <- 10
pval <- 0.01

topMarkers <- markersXie %>%
  filter(p_val_adj < pval) %>%
  group_by(cluster) %>%
  top_n(ntop, avg_log2FC) %>%
  ungroup() %>%
  dplyr::select(gene) %>%
  distinct()

DotPlot(
  xieSeurat.cca,
  features = rev(topMarkers)
  ) +
  theme(axis.text.x = element_text(angle = 45,hjust = 1))

Select Cell Type Module genes from markers

Gene modules selected from the top 200 marker genes at an adjusted pval < 0.01, a mínimum logFC of 0.25, present in our Stromal dataset and not shared between cell types.

celltypeMarkers <- markersXie %>%
  filter(p_val_adj < 0.01) %>%
  filter(avg_log2FC > 0.25) %>%
  filter(gene %in% rownames(bySample.cca)) %>%
  group_by(cluster) %>%
  top_n(n = 100, wt = avg_log2FC) %>%
  # filter(gene %in% rownames(bySample.cca)) %>%
  dplyr::select(cluster,gene) %>%
  filter(cluster != "GM")

whichDup <- unique(sort(celltypeMarkers$gene[which(duplicated(celltypeMarkers$gene))]))
celltypeMarkers <- celltypeMarkers %>% filter(!gene %in% whichDup)

table(celltypeMarkers$cluster)

G0 G4 G2 G1 G3 GM 
24 99 88 25 89  0 
xieSeurat.cca <- ScaleData(xieSeurat.cca,
                    features = unique(sort(c(VariableFeatures(xieSeurat.cca),celltypeMarkers$gene))),
                    verbose = F)
DoHeatmap(xieSeurat.cca, features = celltypeMarkers$gene) + NoLegend()

moduleGeneListXie <- celltypeMarkers %>%
  group_by(cluster) %>%
  group_split()
moduleGeneListXie <- as.list(moduleGeneListXie)
names(moduleGeneListXie) <- levels(factor(celltypeMarkers$cluster))

Cell Type Module Scores

moduleGeneList <- c(moduleGeneListKrenkel,moduleGeneListXie)
for (i in names(moduleGeneList)) {
  bySample.cca <- AddModuleScore(bySample.cca
                             ,features = list(i=moduleGeneList[[i]]$gene)
                             ,name =  paste0(i,".RNAModule")
                             ,assay = "RNA"
                             ,verbose = F
                             )
}

Krenkel Cell Type Module Scores Plots

df <- Embeddings(bySample.cca,reduction = "tsne")
dims <- colnames(df)
df <- cbind(df,bySample.cca@meta.data)
df %>%
  filter(SampleName == "CD11b_WT" & !ManualClustering %in% c("C0","C1","C2","C3","C5","C6","C8","C9","C9b","C10")) %>%
  pivot_longer(cols = contains("X"), names_to = "Module", values_to = "Score") %>%
  mutate(Module=sub(".RNAModule1","",Module)) %>%
  ggplot(aes_string(x=dims[1],y=dims[2],color="Score")) +
  geom_point() +
  scale_color_gradientn(colors = colorRampPalette(c("grey","orange","red"))(3),name="Log(NormCounts)") +
  facet_wrap("Module") +
  theme_classic()

Xie Cell Type Module Scores Plots

df <- Embeddings(bySample.cca,reduction = "tsne")
dims <- colnames(df)
df <- cbind(df,bySample.cca@meta.data)
df %>%
  filter(SampleName == "CD11b_WT" & ManualClustering %in% c("C0","C1","C2","C3","C5","C6","C8","C9","C9b","C10")) %>%
  pivot_longer(cols = contains(c("G0","G1","G2","G3","G4")), names_to = "Module", values_to = "Score") %>%
  mutate(Module=sub(".RNAModule1","",Module)) %>%
  ggplot(aes_string(x=dims[1],y=dims[2],color="Score")) +
  geom_point() +
  scale_color_gradientn(colors = colorRampPalette(c("grey","orange","red"))(3),name="Log(NormCounts)") +
  facet_wrap("Module") +
  theme_classic()

Module Asingments to Clusters

Cluster assigned to the maximum enrichment module score +- 0.02. Some clusters may score two modules or more as maximum.

Analysis based on WT transcriptome only.

Monocyte Krenkel Modules

clustering <- "ManualClustering"
bySample.cca@meta.data %>%
  filter(SampleName == "CD11b_WT" & !ManualClustering %in% c("C0","C1","C2","C3","C5","C6","C8","C9","C9b","C10")) %>%
  dplyr::select(contains(c(clustering,"Module1"))) %>%
  dplyr::select(-contains(c("G0","G1","G2","G3","G4"))) %>%
  pivot_longer(cols = contains("RNAModule1"),names_to = "Module",values_to = "Score") %>%
  rename(Cluster=clustering) %>%
  group_by(Module) %>%
  summarise(Score=scale(Score,center = T,scale = T),Cluster=Cluster) %>%
  group_by(Cluster,Module) %>%
  summarise(AverageScore=mean(Score)) %>%
  group_by(Cluster) %>%
  summarise(Module=Module,AverageScoreScaled=scale(AverageScore),MaxEnrichment=(AverageScore >= (max(AverageScore)-0.02))) %>%
  mutate(Module=sub(".RNAModule1","",Module)) %>%
  mutate(plotBorder=ifelse(MaxEnrichment,1.5,0)) %>%
  ggplot() +
  geom_point(aes_string(x="Module",y="Cluster",fill="AverageScoreScaled",color="MaxEnrichment", stroke = "plotBorder"),size=6,shape=21) +
  scale_fill_gradient(low = "grey90",high = "blue") +
  scale_color_manual(values = c(NA,"red")) +
  theme(axis.text.x = element_text(angle = 45,hjust = 1))
Note: Using an external vector in selections is ambiguous.
i Use `all_of(clustering)` instead of `clustering` to silence this message.
i See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.`summarise()` has grouped output by 'Module'. You can override using the `.groups` argument.`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.

# Assign Sommerkamp cell type to clusters using WT info
clustering <- "ManualClustering"
cellTypeAnnot <- bySample.cca@meta.data %>%
  filter(SampleName == "CD11b_WT" & !ManualClustering %in% c("C0","C1","C2","C3","C5","C6","C8","C9","C9b","C10")) %>%
  dplyr::select(contains(c(clustering,"Module1"))) %>%
  dplyr::select(-contains(c("G0","G1","G2","G3","G4"))) %>%
  pivot_longer(cols = contains("RNAModule1"),names_to = "Module",values_to = "Score") %>%
  rename(Cluster=clustering) %>%
  group_by(Module) %>%
  summarise(Score=scale(Score,center = T,scale = T),Cluster=Cluster) %>%
  group_by(Cluster,Module) %>%
  summarise(AverageScore=mean(Score)) %>%
  group_by(Cluster) %>%
  summarise(Module=Module,AverageScoreScaled=scale(AverageScore),MaxEnrichment=(AverageScore >= (max(AverageScore)-0.02))) %>%
  mutate(Module=sub(".RNAModule1","",Module)) %>%
  mutate(Selected=ifelse(MaxEnrichment,1,0)) %>%
  filter(Selected == 1) %>%
  group_by(Cluster) %>%
  summarise(CellType1 = paste(Module,collapse="_"))

cellTypeAnnot <- cellTypeAnnot %>%
  mutate(CellType1 = sub("\\.","_",sub("^X\\d+\\.","",CellType1,perl=T)))
bySample.cca@meta.data <- bySample.cca@meta.data %>%
  left_join(cellTypeAnnot, by = c("ManualClustering" = "Cluster")) %>% as.data.frame()
rownames(bySample.cca@meta.data) = colnames(bySample.cca)
p1 <- DimPlot(bySample.cca,
              reduction = "tsne",
              group.by = clustering,
              cols = color.list,
              label = T) + theme(legend.position = "none")
p2 <- DimPlot(bySample.cca, 
              reduction = "tsne", 
              group.by = "CellType1",
              cols = color.list,
              label = F)
p1+p2

Neutrophils Xie Modules

clustering <- "ManualClustering"
bySample.cca@meta.data %>%
  filter(SampleName == "CD11b_WT" & ManualClustering %in% c("C0","C1","C2","C3","C5","C6","C8","C9","C9b","C10")) %>%
  dplyr::select(contains(c(clustering,"G0","G1","G2","G3","G4"))) %>%
  pivot_longer(cols = contains("RNAModule1"),names_to = "Module",values_to = "Score") %>%
  rename(Cluster=clustering) %>%
  group_by(Module) %>%
  summarise(Score=scale(Score,center = T,scale = T),Cluster=Cluster) %>%
  group_by(Cluster,Module) %>%
  summarise(AverageScore=mean(Score)) %>%
  group_by(Cluster) %>%
  summarise(Module=Module,AverageScoreScaled=scale(AverageScore),MaxEnrichment=(AverageScore >= (max(AverageScore)-0.02))) %>%
  mutate(Module=sub(".RNAModule1","",Module)) %>%
  mutate(plotBorder=ifelse(MaxEnrichment,1.5,0)) %>%
  ggplot() +
  geom_point(aes_string(x="Module",y="Cluster",fill="AverageScoreScaled",color="MaxEnrichment", stroke = "plotBorder"),size=6,shape=21) +
  scale_fill_gradient(low = "grey90",high = "blue") +
  scale_color_manual(values = c(NA,"red")) +
  theme(axis.text.x = element_text(angle = 45))
`summarise()` has grouped output by 'Module'. You can override using the `.groups` argument.`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.

# Assign Sommerkamp cell type to clusters using WT info
clustering <- "ManualClustering"
cellTypeAnnot <- bySample.cca@meta.data %>%
  filter(SampleName == "CD11b_WT" & ManualClustering %in% c("C0","C1","C2","C3","C5","C6","C8","C9","C9b","C10")) %>%
  dplyr::select(contains(c(clustering,"G0","G1","G2","G3","G4"))) %>%
  pivot_longer(cols = contains("RNAModule1"),names_to = "Module",values_to = "Score") %>%
  rename(Cluster=clustering) %>%
  group_by(Module) %>%
  summarise(Score=scale(Score,center = T,scale = T),Cluster=Cluster) %>%
  group_by(Cluster,Module) %>%
  summarise(AverageScore=mean(Score)) %>%
  group_by(Cluster) %>%
  summarise(Module=Module,AverageScoreScaled=scale(AverageScore),MaxEnrichment=(AverageScore >= (max(AverageScore)-0.02))) %>%
  mutate(Module=sub(".RNAModule1","",Module)) %>%
  mutate(Selected=ifelse(MaxEnrichment,1,0)) %>%
  filter(Selected == 1) %>%
  group_by(Cluster) %>%
  summarise(CellType2 = paste(Module,collapse="_"))
bySample.cca@meta.data <- bySample.cca@meta.data %>%
  left_join(cellTypeAnnot, by = c("ManualClustering" = "Cluster")) %>% as.data.frame()
rownames(bySample.cca@meta.data) = colnames(bySample.cca)
p1 <- DimPlot(bySample.cca,
              reduction = "tsne",
              group.by = clustering,
              cols = color.list,
              label = T) + theme(legend.position = "none")
p2 <- DimPlot(bySample.cca, 
              reduction = "tsne", 
              group.by = "CellType2",
              cols = color.list,
              label = F)
p1+p2

bySample.cca@meta.data <- bySample.cca@meta.data  %>%
  mutate(CellType1 = ifelse(is.na(CellType1),CellType2,CellType1)) %>% dplyr::select(-CellType2)

Cell Type By Cluster

p1 <- DimPlot(bySample.cca,
              reduction = "tsne",
              group.by = clustering,
              cols = color.list,
              label = T) + theme(legend.position = "none")
p2 <- DimPlot(bySample.cca, 
              reduction = "tsne", 
              group.by = "CellType1",
              cols = color.list,
              label = F)
p1+p2

Cell Type By Condition

DimPlot(bySample.cca,
        reduction = "tsne",
        group.by = "CellType1",
        cols = color.list,
        split.by = "SampleName")

Cell Type Proportions

bySample.cca@meta.data %>%
  ggplot(aes(x="",fill=CellType1)) +
  geom_bar(stat="count",position="fill") +
  coord_polar("y",start = 0) +
  scale_fill_manual(values = color.list) +
  facet_wrap("SampleName") +
  theme(axis.line = element_blank(),
        axis.text = element_blank(),
        axis.title = element_blank(),
        panel.background = element_blank())

bySample.cca@meta.data %>%
  group_by(SampleName) %>%
  count(
    CellType1
  ) %>%
  pivot_wider(id_cols = SampleName, names_from = CellType1, values_from = n)

Marker Genes for each cell type

Based on WT transcriptomes.

clustering <- "CellType1"

Idents(bySample.cca) <- clustering

minPct <- 30
pval <- 0.01
useAssay <- "RNA"
method <- "wilcox"

DefaultAssay(bySample.cca) <- useAssay
bySample.cca.WT <- subset(bySample.cca, SampleName == "CD11b_WT")
markers <- bySample.cca.WT %>%
  FindAllMarkers(
    assay = useAssay
    , slot = "data"
    , min.pct = minPct/100
    , return.thresh = pval
    , test.use = method
    , verbose = F
    , only.pos = T
  )
markers %>%
  filter(p_val_adj < pval) %>%
  group_by(cluster) %>%
  count()

Top 30 Markers Heatmap

ntop <- 30

topMarkers <- markers %>% 
  filter(p_val_adj < pval) %>%
  group_by(cluster) %>%
  top_n(ntop, avg_log2FC)

bySample.cca <- bySample.cca %>%
  ScaleData(features = c(VariableFeatures(.),topMarkers$gene),
            assay = useAssay,
            verbose = F)

bySample.cca %>%
  DoHeatmap(assay = useAssay,
            features = topMarkers$gene,
            group.colors = color.list)

Top 9 Markers Expression

ntop <- 9
for (myCluster in levels(markers$cluster)) {
  topMarkers <- markers %>% 
  filter(p_val_adj < pval & cluster == myCluster) %>%
  top_n(ntop, avg_log2FC)
  
  print(bySample.cca %>%
    FeaturePlot(features = topMarkers$gene,
                reduction = "tsne") +
      NoLegend() +
      plot_annotation(
        title = paste("Markers for cell type:",myCluster
                      )
))
}

Top 10 Markers DotPlot

ntop <- 10
pvalcut <- 0.01

topMarkers <- markers %>%
  filter(p_val_adj < pval) %>%
  group_by(cluster) %>%
  top_n(ntop, avg_log2FC) %>%
  ungroup() %>%
  dplyr::select(gene) %>%
  distinct()

DotPlot(
  bySample.cca,
  assay = useAssay,
  group.by = clustering,
  features = rev(topMarkers)
  ) +
  theme(axis.text.x = element_text(angle = 45,hjust = 1))

Differences between Conditions for each Cell Type

Within each Cell Type we have tested the differences between KO and WT conditions.

tde.files <- NULL
contrastByCond <- list()
useAssay <- "RNA"
clustering <- "CellType1"
testUse <- "wilcox"
minPct <- 0.1

cond1 <- "CD11b_KO"
cond2 <- "CD11b_WT"

DefaultAssay(bySample.cca) <- useAssay

bySample.cca <- bySample.cca %>%
  SetIdent(value = paste(bySample.cca$CellType1,bySample.cca$SampleName,sep="."))

deGenes <- list()
for (myCluster in levels(factor(bySample.cca$CellType1))) {
        
        ident1 <- paste(myCluster,cond1,sep=".")
        ident2 <- paste(myCluster,cond2,sep=".")
        
        deGenes[[myCluster]] <- bySample.cca %>%
          FindMarkers(
            assay = useAssay
            , slot = "data"
            , ident.1 = ident1
            , ident.2 = ident2
            , min.pct = minPct
            , test.use = testUse
            , verbose = F
          ) %>%
          mutate(gene = rownames(.),
                 cluster = myCluster)
}
Idents(bySample.cca) <- clustering
pval <- 0.01

deGenes <- do.call(rbind,deGenes)

deGenes %>%
  filter(p_val_adj < pval) %>%
  group_by(cluster) %>%
  count()

Top 10 Differences DotPlot

ntop <- 10
pvalcut <- 0.01

topDeGenes <- deGenes %>%
  filter(p_val_adj < pval) %>%
  group_by(cluster) %>%
  top_n(ntop, avg_log2FC) %>%
  ungroup() %>%
  dplyr::select(gene) %>%
  distinct()

DotPlot(
  bySample.cca,
  assay = "RNA",
  group.by = clustering,
  features = rev(topDeGenes),
  split.by = "SampleName"
  ) +
  theme(axis.text.x = element_text(angle = 45,hjust = 1))

Il1B and Il1rn Expression

df <- Embeddings(bySample.cca,reduction = "tsne")
dnames <- colnames(df)
df <- cbind(df,bySample.cca@meta.data)
df <- cbind(df,FetchData(bySample.cca,vars = c("Il1b","Il1rn")))
df %>%
  arrange(Il1b) %>%
  ggplot(aes_string(x=dnames[1],y=dnames[2])) +
  geom_point(aes_string(color="Il1b")) +
  facet_wrap("SampleName") +
  ggtitle("Il1b") +
  scale_color_gradientn(colors = colorRampPalette(c("grey","orange","red"))(3),name="Log(NormCounts)") +
  theme_classic()

df %>%
  arrange(Il1rn) %>%
  ggplot(aes_string(x=dnames[1],y=dnames[2])) +
  geom_point(aes_string(color="Il1rn")) +
  facet_wrap("SampleName") +
  ggtitle("Il1rn") +
  scale_color_gradientn(colors = colorRampPalette(c("grey","orange","red"))(3),name="Log(NormCounts)") +
  theme_classic()

df %>%
  mutate(Il1Exp = case_when(
    Il1b>0 & Il1rn == 0 ~ "Il1b only",
    Il1b==0 & Il1rn > 0 ~ "Il1rn only",
    Il1b>0 & Il1rn > 0 ~ "Il1b and Il1rn")) %>%
 ggplot(aes(x="",fill=Il1Exp)) +
  geom_bar(stat="count",position="fill") +
  coord_polar("y",start = 0) +
  scale_fill_manual(values = color.list) +
  facet_wrap(c("SampleName","CellType1"),ncol = 6) +
  theme(axis.line = element_blank(),
        axis.text = element_blank(),
        axis.title = element_blank(),
        panel.background = element_blank())

Il1b vs Il1rn

p <- df %>%
  filter(SampleName == "CD11b_WT")  %>%
  mutate(Il1Exp = case_when(
    Il1b>0 & Il1rn == 0 ~ "Il1b only",
    Il1b==0 & Il1rn > 0 ~ "Il1rn only",
    Il1b>0 & Il1rn > 0 ~ "Il1b and Il1rn")) %>%
  ggplot(aes(x=Il1rn,y=Il1b)) +
  ggtitle("Myeloid WT Cells") +
  geom_point() +
  theme_classic()

ggMarginal(p,type = "densigram")

Violin plots on Selected Genes

selectedGenes <- c("Lyn","Hif1a","Lmo4","Csf2rb","Myd88","Cxcr2","Nfkbia","Cebpb")
df <- FetchData(bySample.cca,vars = selectedGenes)
df <- cbind(df,bySample.cca@meta.data)
df <- df %>% mutate(SampleName=factor(SampleName,levels = c("CD11b_WT","CD11b_KO")))
library(stringr)
pList <- list()
for (gene in selectedGenes) {
  pList[[length(pList)+1]] <- ggplot(df,aes_string(y=gene,x="SampleName",color="SampleName",fill="SampleName")) +
    geom_violin(stat = "ydensity",alpha=0.2) +
    # geom_jitter(alpha=1) +
    scale_color_manual(values = c("grey40","#a80a0b")) +
    scale_fill_manual(values = c("grey40","#a80a0b")) +
    stat_summary(fun = "mean",
               geom = "crossbar", 
               width = 0.5,
               colour = "black") +
    ylab("Expression Level log(NormCounts+1)") +
    ggtitle(str_to_title(gene)) +
    facet_wrap("CellType1",nrow = 1) +
    theme_classic() +
    theme(axis.text.x = element_text(angle = 90),legend.position = "none")
}

p <- lapply(pList,plot)

Session Info

sessionInfo()
R version 4.1.1 (2021-08-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19042)

Matrix products: default

locale:
[1] LC_COLLATE=English_United Kingdom.1252 
[2] LC_CTYPE=English_United Kingdom.1252   
[3] LC_MONETARY=English_United Kingdom.1252
[4] LC_NUMERIC=C                           
[5] LC_TIME=English_United Kingdom.1252    

attached base packages:
[1] parallel  stats4    stats     graphics  grDevices utils    
[7] datasets  methods   base     

other attached packages:
 [1] celldex_1.2.0               SingleR_1.6.1              
 [3] patchwork_1.1.1             ggridges_0.5.3             
 [5] ggExtra_0.10.0              gtools_3.9.2               
 [7] circlize_0.4.13             openxlsx_4.2.4             
 [9] RColorBrewer_1.1-2          forcats_0.5.1              
[11] stringr_1.4.0               dplyr_1.0.8                
[13] purrr_0.3.4                 readr_2.1.2                
[15] tidyr_1.2.0                 tibble_3.1.6               
[17] ggplot2_3.3.5               tidyverse_1.3.1            
[19] scDblFinder_1.6.0           scran_1.20.1               
[21] scuttle_1.2.1               SingleCellExperiment_1.14.1
[23] SummarizedExperiment_1.22.0 Biobase_2.52.0             
[25] GenomicRanges_1.44.0        GenomeInfoDb_1.28.4        
[27] IRanges_2.26.0              S4Vectors_0.30.2           
[29] BiocGenerics_0.38.0         MatrixGenerics_1.4.3       
[31] matrixStats_0.61.0          SeuratObject_4.0.2         
[33] Seurat_4.0.5               

loaded via a namespace (and not attached):
  [1] utf8_1.2.2                   
  [2] reticulate_1.22              
  [3] tidyselect_1.1.2             
  [4] RSQLite_2.2.8                
  [5] AnnotationDbi_1.54.1         
  [6] htmlwidgets_1.5.4            
  [7] grid_4.1.1                   
  [8] BiocParallel_1.26.2          
  [9] Rtsne_0.15                   
 [10] munsell_0.5.0                
 [11] ScaledMatrix_1.0.0           
 [12] codetools_0.2-18             
 [13] ica_1.0-2                    
 [14] statmod_1.4.37               
 [15] xgboost_1.6.0.1              
 [16] future_1.22.1                
 [17] miniUI_0.1.1.1               
 [18] withr_2.5.0                  
 [19] colorspace_2.0-2             
 [20] filelock_1.0.2               
 [21] knitr_1.36                   
 [22] rstudioapi_0.13              
 [23] ROCR_1.0-11                  
 [24] tensor_1.5                   
 [25] listenv_0.8.0                
 [26] labeling_0.4.2               
 [27] GenomeInfoDbData_1.2.6       
 [28] polyclip_1.10-0              
 [29] farver_2.1.0                 
 [30] bit64_4.0.5                  
 [31] parallelly_1.28.1            
 [32] vctrs_0.4.0                  
 [33] generics_0.1.2               
 [34] xfun_0.27                    
 [35] BiocFileCache_2.0.0          
 [36] R6_2.5.1                     
 [37] ggbeeswarm_0.6.0             
 [38] rsvd_1.0.5                   
 [39] locfit_1.5-9.4               
 [40] bitops_1.0-7                 
 [41] spatstat.utils_2.2-0         
 [42] cachem_1.0.6                 
 [43] DelayedArray_0.18.0          
 [44] assertthat_0.2.1             
 [45] promises_1.2.0.1             
 [46] scales_1.1.1                 
 [47] beeswarm_0.4.0               
 [48] gtable_0.3.0                 
 [49] beachmat_2.8.1               
 [50] globals_0.14.0               
 [51] goftest_1.2-3                
 [52] rlang_1.0.2                  
 [53] GlobalOptions_0.1.2          
 [54] splines_4.1.1                
 [55] lazyeval_0.2.2               
 [56] broom_0.7.9                  
 [57] spatstat.geom_2.3-0          
 [58] modelr_0.1.8                 
 [59] BiocManager_1.30.16          
 [60] yaml_2.2.1                   
 [61] reshape2_1.4.4               
 [62] abind_1.4-5                  
 [63] backports_1.3.0              
 [64] httpuv_1.6.3                 
 [65] tools_4.1.1                  
 [66] ellipsis_0.3.2               
 [67] spatstat.core_2.3-0          
 [68] Rcpp_1.0.7                   
 [69] plyr_1.8.6                   
 [70] sparseMatrixStats_1.4.2      
 [71] zlibbioc_1.38.0              
 [72] RCurl_1.98-1.5               
 [73] rpart_4.1-15                 
 [74] deldir_1.0-6                 
 [75] pbapply_1.5-0                
 [76] viridis_0.6.2                
 [77] cowplot_1.1.1                
 [78] zoo_1.8-9                    
 [79] haven_2.4.3                  
 [80] ggrepel_0.9.1                
 [81] cluster_2.1.2                
 [82] fs_1.5.0                     
 [83] magrittr_2.0.1               
 [84] RSpectra_0.16-0              
 [85] data.table_1.14.2            
 [86] scattermore_0.7              
 [87] reprex_2.0.1                 
 [88] lmtest_0.9-38                
 [89] RANN_2.6.1                   
 [90] fitdistrplus_1.1-6           
 [91] hms_1.1.1                    
 [92] mime_0.12                    
 [93] xtable_1.8-4                 
 [94] readxl_1.3.1                 
 [95] shape_1.4.6                  
 [96] gridExtra_2.3                
 [97] compiler_4.1.1               
 [98] scater_1.20.1                
 [99] KernSmooth_2.23-20           
[100] crayon_1.5.1                 
[101] htmltools_0.5.2              
[102] tzdb_0.3.0                   
[103] mgcv_1.8-36                  
[104] later_1.3.0                  
[105] lubridate_1.8.0              
[106] DBI_1.1.1                    
[107] ExperimentHub_2.0.0          
[108] dbplyr_2.1.1                 
[109] MASS_7.3-54                  
[110] rappdirs_0.3.3               
[111] Matrix_1.3-4                 
[112] cli_3.2.0                    
[113] metapod_1.0.0                
[114] igraph_1.2.7                 
[115] pkgconfig_2.0.3              
[116] plotly_4.10.0                
[117] spatstat.sparse_2.0-0        
[118] xml2_1.3.3                   
[119] vipor_0.4.5                  
[120] dqrng_0.3.0                  
[121] XVector_0.32.0               
[122] rvest_1.0.2                  
[123] digest_0.6.28                
[124] sctransform_0.3.2            
[125] RcppAnnoy_0.0.19             
[126] spatstat.data_2.1-0          
[127] Biostrings_2.60.2            
[128] cellranger_1.1.0             
[129] leiden_0.3.9                 
[130] uwot_0.1.10                  
[131] edgeR_3.34.1                 
[132] DelayedMatrixStats_1.14.3    
[133] curl_4.3.2                   
[134] shiny_1.7.1                  
[135] lifecycle_1.0.1              
[136] nlme_3.1-152                 
[137] jsonlite_1.8.0               
[138] BiocNeighbors_1.10.0         
[139] viridisLite_0.4.0            
[140] limma_3.48.3                 
[141] fansi_1.0.3                  
[142] pillar_1.7.0                 
[143] lattice_0.20-44              
[144] KEGGREST_1.32.0              
[145] fastmap_1.1.0                
[146] httr_1.4.2                   
[147] survival_3.2-11              
[148] interactiveDisplayBase_1.30.0
[149] glue_1.6.2                   
[150] zip_2.2.0                    
[151] png_0.1-7                    
[152] bluster_1.2.1                
[153] BiocVersion_3.13.1           
[154] bit_4.0.4                    
[155] stringi_1.7.6                
[156] blob_1.2.2                   
[157] BiocSingular_1.8.1           
[158] AnnotationHub_3.0.2          
[159] memoise_2.0.0                
[160] irlba_2.3.3                  
[161] future.apply_1.8.1           
LS0tDQp0aXRsZTogIk15ZWxvaWQgQW5hbHlzaXMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc3VwcHJlc3NXYXJuaW5ncyhsaWJyYXJ5KFNldXJhdCkpDQpzdXBwcmVzc1dhcm5pbmdzKGxpYnJhcnkoc2NyYW4pKQ0Kc3VwcHJlc3NXYXJuaW5ncyhsaWJyYXJ5KHNjRGJsRmluZGVyKSkNCnN1cHByZXNzV2FybmluZ3MobGlicmFyeSh0aWR5dmVyc2UpKQ0Kc3VwcHJlc3NXYXJuaW5ncyhsaWJyYXJ5KFJDb2xvckJyZXdlcikpDQpzdXBwcmVzc1dhcm5pbmdzKGxpYnJhcnkob3Blbnhsc3gpKQ0Kc3VwcHJlc3NXYXJuaW5ncyhsaWJyYXJ5KGNpcmNsaXplKSkNCnN1cHByZXNzV2FybmluZ3MobGlicmFyeShndG9vbHMpKQ0Kc3VwcHJlc3NXYXJuaW5ncyhsaWJyYXJ5KGdnRXh0cmEpKQ0Kc3VwcHJlc3NXYXJuaW5ncyhsaWJyYXJ5KGdncmlkZ2VzKSkNCnN1cHByZXNzV2FybmluZ3MobGlicmFyeShwYXRjaHdvcmspKQ0KDQpwcm9qZWN0RGlyIDwtICIuIg0KcHJvamVjdFBhdGggPC0gZmlsZS5wYXRoKHByb2plY3REaXIpDQpvdXRwdXRQYXRoIDwtIGZpbGUucGF0aChwcm9qZWN0UGF0aCkNCmRhdGFEaXIgPC0gImRhdGEiDQpkYXRhUGF0aCA8LSBmaWxlLnBhdGgocHJvamVjdFBhdGgsZGF0YURpcikNCnByZWZpeCA8LSAiTXllbG9pZCINCg0KZGlyLmNyZWF0ZShmaWxlLnBhdGgob3V0cHV0UGF0aCkscmVjdXJzaXZlID0gVCkNCg0Kc2V0d2Qob3V0cHV0UGF0aCkNCg0KY29sb3IubGlzdCA8LSBjKCIjZWJhYzIzIiwgIiNiODAwNTgiLCAiIzAwOGNmOSIsDQogICAgICAgICAgICAgICAgIiMwMDZlMDAiLCAiIzAwYmJhZCIsICIjZDE2M2U2IiwNCiAgICAgICAgICAgICAgICAiI2IyNDUwMiIsICIjZmY5Mjg3IiwgIiM1OTU0ZDYiLA0KICAgICAgICAgICAgICAgICIjMDBjNmY4IiwgIiM4Nzg1MDAiLCAiIzAwYTc2YyIsDQogICAgICAgICAgICAgICAgIiNiZGJkYmQiLCAiIzg0NmI1NCIsDQogICAgICAgICAgICAgICAgYnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpLA0KICAgICAgICAgICAgICAgIGJyZXdlci5wYWwoMTIsICJTZXQzIiksDQogICAgICAgICAgICAgICAgYnJld2VyLnBhbCg4LCAiUGFzdGVsMiIpLA0KICAgICAgICAgICAgICAgIGNvbG9yUmFtcFBhbGV0dGUoYygiZ3JleTIwIiwiZ3JleTcwIikpKDQpKQ0KYGBgDQoNCiMgTG9hZCBDb3VudHMNCg0KYGBge3J9DQpzYyA8LSBSZWFkMTBYKGZpbGUucGF0aChkYXRhUGF0aCwibXllbG9pZC5jb3VudHMiKSkNCnNjIDwtIENyZWF0ZVNldXJhdE9iamVjdCgNCiAgY291bnRzID0gc2MsDQogIGFzc2F5ID0gIlJOQSIsDQogIHByb2plY3QgPSAiTXllbG9pZCIsDQogIG5hbWVzLmZpZWxkID0gYygxLDIpLA0KICBuYW1lcy5kZWxpbSA9ICJfIiwNCiAgbWluLmNlbGxzID0gMCwNCiAgbWluLmZlYXR1cmVzID0gMA0KKQ0Kc2NAbWV0YS5kYXRhJFNhbXBsZU5hbWUgPC0gc2NAbWV0YS5kYXRhJG9yaWcuaWRlbnQNCg0KdGFibGUoc2MkU2FtcGxlTmFtZSkNCmBgYA0KDQojIEludGVncmF0aW9uIGJ5IFNhbXBsZSB1c2luZyBDQ0ENCg0KYGBge3J9DQpjZWxsU2V0IDwtICJNeWVsb2lkIg0Kc3Vic2V0TmFtZSA8LSAiSW50ZWdyYXRlZCINCmBgYA0KDQpgYGB7cn0NCm5mZWF0dXJlcyA8LSAyMDAwDQpyZXNzIDwtIGMoMC41KQ0KbnBjcyA8LSAyMA0KDQpieVNhbXBsZSA8LSBTcGxpdE9iamVjdChzYywgc3BsaXQuYnkgPSAiU2FtcGxlTmFtZSIpDQoNCiMgSW50ZWdyYXRpb24gdmlhIENDQSBwcm90b2NvbA0KYnlTYW1wbGUgPC0gbGFwcGx5KGJ5U2FtcGxlLCBmdW5jdGlvbiAoeCkgew0KICB4IDwtIHggJT4lIE5vcm1hbGl6ZURhdGEodmVyYm9zZT1GKSAlPiUNCiAgICBGaW5kVmFyaWFibGVGZWF0dXJlcyhuZmVhdHVyZXMgPSBuZmVhdHVyZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1GKSAlPiUNCiAgICBTY2FsZURhdGEodmVyYm9zZT1GKSAlPiUNCiAgICBSdW5QQ0EodmVyYm9zZT1GKQ0KICByZXR1cm4oeCkNCn0pDQoNCmNvbW1vbkZlYXR1cmVzIDwtIFNlbGVjdEludGVncmF0aW9uRmVhdHVyZXMoYnlTYW1wbGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2U9RikNCg0Kc21hbmNob3JzIDwtIEZpbmRJbnRlZ3JhdGlvbkFuY2hvcnMoYnlTYW1wbGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmNob3IuZmVhdHVyZXMgPSBjb21tb25GZWF0dXJlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2U9RikNCg0KYnlTYW1wbGUuY2NhIDwtIEludGVncmF0ZURhdGEoYW5jaG9yc2V0ID0gc21hbmNob3JzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMudG8uaW50ZWdyYXRlID0gcm93bmFtZXMoYnlTYW1wbGVbWzFdXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlPUYpDQoNCkRlZmF1bHRBc3NheShieVNhbXBsZS5jY2EpIDwtICJpbnRlZ3JhdGVkIg0KDQojIFJ1biB0aGUgc3RhbmRhcmQgd29ya2Zsb3cgZm9yIGNsdXN0ZXJpbmcgYW5kIHZpc3VhbGl6YXRpb24gb2YgaW50ZWdyYXRlZCBkYXRhDQpieVNhbXBsZS5jY2EgPC0gYnlTYW1wbGUuY2NhICU+JQ0KICBTY2FsZURhdGEodmVyYm9zZSA9IEZBTFNFKSAlPiUNCiAgUnVuUENBKHZlcmJvc2UgPSBGQUxTRSkgJT4lDQogIEZpbmROZWlnaGJvcnMocmVkdWN0aW9uID0gInBjYSIsDQogICAgICAgICAgICAgICAgZGltcyA9IHNlcShucGNzKSwNCiAgICAgICAgICAgICAgICBmb3JjZS5yZWNhbGMgPSBULA0KICAgICAgICAgICAgICAgIHZlcmJvc2U9RikgJT4lDQogIEZpbmRDbHVzdGVycyhyZXNvbHV0aW9uID0gcmVzcywNCiAgICAgICAgICAgICAgIHZlcmJvc2U9RikgJT4lDQogIFJ1blVNQVAocmVkdWN0aW9uID0gInBjYSIsDQogICAgICAgICAgZGltcyA9IHNlcShucGNzKSwNCiAgICAgICAgICB2ZXJib3NlPUYpICU+JQ0KICBSdW5UU05FKHJlZHVjdGlvbiA9ICJwY2EiLA0KICAgICAgICAgIGRpbXMgPSBzZXEobnBjcyksDQogICAgICAgICAgcGVycGxleGl0eT0xMDAsDQogICAgICAgICAgdmVyYm9zZT1GKQ0KDQojIFJlYW5ub3RhdGUgY2x1c3RlcnMNCmJ5U2FtcGxlLmNjYUBtZXRhLmRhdGEgPC0gYnlTYW1wbGUuY2NhQG1ldGEuZGF0YSAlPiUNCiAgbXV0YXRlKGFjcm9zcyhzdGFydHNfd2l0aCgiaW50ZWdyYXRlZF8iKSwuZm5zID0gZnVuY3Rpb24gKHgpe3Bhc3RlMCgiQyIseCl9KSkgJT4lDQogIG11dGF0ZShhY3Jvc3Moc3RhcnRzX3dpdGgoImludGVncmF0ZWRfIiksLmZucyA9IGZ1bmN0aW9uICh4KXtmYWN0b3IoeCl9KSkgJT4lDQogIG11dGF0ZShhY3Jvc3Moc3RhcnRzX3dpdGgoImludGVncmF0ZWRfIiksLmZucyA9IGZ1bmN0aW9uICh4KXtmYWN0b3IoeCxsZXZlbHM9bWl4ZWRzb3J0KGxldmVscyh4KSkpfSkpDQoNCnNhdmVSRFMoYnlTYW1wbGUuY2NhLGZpbGUgPSBmaWxlLnBhdGgob3V0cHV0UGF0aCxwYXN0ZShjZWxsU2V0LHN1YnNldE5hbWUsInNldXJhdCIsInJkcyIsc2VwID0gIi4iKSkpDQoNCmJ5U2FtcGxlLmNjYQ0KYGBgDQoNCmBgYHtyfQ0KY2x1c3RlcmluZyA8LSAiaW50ZWdyYXRlZF9zbm5fcmVzLjAuNSINCmBgYA0KDQpgYGB7cn0NCnAxIDwtIERpbVBsb3QoYnlTYW1wbGUuY2NhLA0KICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidHNuZSIsDQogICAgICAgICAgICAgIGdyb3VwLmJ5ID0gIlNhbXBsZU5hbWUiLA0KICAgICAgICAgICAgICBjb2xzID0gY29sb3IubGlzdCkNCnAyIDwtIERpbVBsb3QoYnlTYW1wbGUuY2NhLA0KICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidHNuZSIsDQogICAgICAgICAgICAgIGdyb3VwLmJ5ID0gY2x1c3RlcmluZywNCiAgICAgICAgICAgICAgY29scyA9IGNvbG9yLmxpc3QsDQogICAgICAgICAgICAgIGxhYmVsID0gVCkNCnAxICsgcDINCmBgYA0KDQpgYGB7cn0NCkRpbVBsb3QoYnlTYW1wbGUuY2NhLA0KICAgICAgICByZWR1Y3Rpb24gPSAidHNuZSIsDQogICAgICAgIGdyb3VwLmJ5ID0gY2x1c3RlcmluZywNCiAgICAgICAgY29scyA9IGNvbG9yLmxpc3QsDQogICAgICAgIHNwbGl0LmJ5ID0gIlNhbXBsZU5hbWUiKQ0KYGBgDQoNCiMjIENlbGwgSWRlbnRpZmljYXRpb24gYmFzZWQgaW4gU2luZ2xlUiBmcmFtZXdvcmsNCg0KQ2x1c3RlcnMgYW5kIENlbGxzIGFyZSBjbGFzc2lmaWVkIGJhc2VkIG9uIFNpbmdsZVIgbWV0aG9kIHVzaW5nIEJsdWVwcmludCBFbmNvZGUsIHRoZSBIdW1hbiBQcmltYXJ5IENlbGwgQXRsYXMgYW5kIHRoZSBNb25hY28gSW1tdW5lIGNlbGwgdHlwZSBwcm9maWxlcyBjb2xsZWN0aW9uLiBUaGlzIGlkZW50aWZpY2F0aW9uIGlzIG5vdCBwcmVjaXNlIGFuZCBzaG91bGQgYmUgaW50ZXJwcmV0ZWQgd2l0aCBjYXV0aW9uLg0KDQpgYGB7ciB9DQpsaWJyYXJ5KFNpbmdsZVIpDQp1c2VBc3NheSA8LSAiUk5BIg0KDQpjbHVzdGVyaW5nIDwtICJpbnRlZ3JhdGVkX3Nubl9yZXMuMC41Ig0KSWRlbnRzKGJ5U2FtcGxlLmNjYSkgPC0gY2x1c3RlcmluZw0KDQpieVNhbXBsZS5jY2EuV1QgPC0gc3Vic2V0KGJ5U2FtcGxlLmNjYSxTYW1wbGVOYW1lID09ICJDRDExYl9XVCIpDQoNCm5Db3JlcyA8LSAxDQoNCmltbUdlbj1jZWxsZGV4OjpJbW1HZW5EYXRhKCkNCg0KDQpzaW5nbGVyIDwtIGxpc3QoDQogIGJ5Q2x1c3RlciA9IGxpc3QoKQ0KKQ0KDQpzaW5nbGVyIDwtIFNpbmdsZVIoDQogICAgY2x1c3RlcnMgPSBJZGVudHMoYnlTYW1wbGUuY2NhLldUKQ0KICAgICwgdGVzdCA9IFNldXJhdDo6QXNzYXlzKGJ5U2FtcGxlLmNjYS5XVCxzbG90ID0gdXNlQXNzYXkpQGRhdGENCiAgICAsIHJlZiA9IGltbUdlbg0KICAgICwgbGFiZWxzID0gaW1tR2VuJGxhYmVsLmZpbmUNCiAgICAsIGdlbmVzID0gImRlIg0KICAgICwgcXVhbnRpbGUgPSAwLjgNCiAgICAsIGZpbmUudHVuZSA9IFQNCiAgICAsIHR1bmUudGhyZXNoID0gMC4wNQ0KICAgICwgc2QudGhyZXNoID0gMQ0KICApDQoNCmJ5U2FtcGxlLmNjYUBtZXRhLmRhdGEgPC0gYnlTYW1wbGUuY2NhQG1ldGEuZGF0YSAlPiUNCiAgbXV0YXRlKGltbUdlbkxhYmVscyA9IHNpbmdsZXJbaW50ZWdyYXRlZF9zbm5fcmVzLjAuNSwiZmlyc3QubGFiZWxzIl0sDQogICAgICAgICBpbW1HZW5GVExhYmVscyA9IHNpbmdsZXJbaW50ZWdyYXRlZF9zbm5fcmVzLjAuNSwibGFiZWxzIl0pDQoNCnNhdmVSRFMoYnlTYW1wbGUuY2NhLGZpbGUgPSBmaWxlLnBhdGgob3V0cHV0UGF0aCxwYXN0ZShjZWxsU2V0LHN1YnNldE5hbWUsInNldXJhdCIsInJkcyIsc2VwPSIuIikpKSAgDQpgYGANCg0KYGBge3J9DQpwMSA8LSBEaW1QbG90KGJ5U2FtcGxlLmNjYSwNCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInRzbmUiLA0KICAgICAgICAgICAgICBncm91cC5ieSA9ICJpbW1HZW5GVExhYmVscyIsDQogICAgICAgICAgICAgIGNvbHMgPSBjb2xvci5saXN0KQ0KcDIgPC0gRGltUGxvdChieVNhbXBsZS5jY2EsDQogICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ0c25lIiwNCiAgICAgICAgICAgICAgZ3JvdXAuYnkgPSBjbHVzdGVyaW5nLA0KICAgICAgICAgICAgICBjb2xzID0gY29sb3IubGlzdCwNCiAgICAgICAgICAgICAgbGFiZWwgPSBUKQ0KcDEgKyBwMg0KYGBgDQoNCmBgYHtyfQ0KZGYgPC0gRW1iZWRkaW5ncyhieVNhbXBsZS5jY2EscmVkdWN0aW9uID0gInRzbmUiKQ0KZGYgPC0gY2JpbmQoZGYsYnlTYW1wbGUuY2NhQG1ldGEuZGF0YSkNCmRmIDwtIGRmICU+JQ0KICBtdXRhdGUoTWFudWFsQ2x1c3RlcmluZyA9IGZhY3RvcihpZmVsc2UoDQogICAgdFNORV8xID4gLTI1LjUgJiBpbnRlZ3JhdGVkX3Nubl9yZXMuMC41ID09ICJDOSIgLCJDOWIiLGFzLmNoYXJhY3RlcihpbnRlZ3JhdGVkX3Nubl9yZXMuMC41KQ0KICAgICkpKSAlPiUNCiAgbXV0YXRlKE1hbnVhbENsdXN0ZXJpbmcgPSBmYWN0b3IoTWFudWFsQ2x1c3RlcmluZyxsZXZlbHM9bWl4ZWRzb3J0KGxldmVscyhNYW51YWxDbHVzdGVyaW5nKSkpKQ0KYnlTYW1wbGUuY2NhQG1ldGEuZGF0YSRNYW51YWxDbHVzdGVyaW5nIDwtIGRmJE1hbnVhbENsdXN0ZXJpbmcNCkRpbVBsb3QoYnlTYW1wbGUuY2NhLA0KICAgICAgICByZWR1Y3Rpb24gPSAidHNuZSIsDQogICAgICAgIGdyb3VwLmJ5ID0gIk1hbnVhbENsdXN0ZXJpbmciLA0KICAgICAgICBjb2xzID0gY29sb3IubGlzdCxsYWJlbCA9IFQscHQuc2l6ZSA9IDIpDQpgYGANCg0KIyMgR2V0IE1vbm9jeXRlIE1vZHVsZXMgZnJvbSBLcmVua2VsIGV0IGFsIHB1YmxpY2F0aW9uDQoNCkRhdGEgb2J0YWluZWQgZnJvbSBbS3JlbmtlbCBldC4gYWwuIEd1dCAyMDIwXShodHRwOi8vZHguZG9pLm9yZy8xMC4xMTM2L2d1dGpubC0yMDE5LTMxODM4MikuIERhdGFzZXQgR1NFMTMxODM0LiBDbHVzdGVyaXplZCB1c2luZyBzdGFuZGFyZCBwcm9jZWR1cmVzICgyMDAwIHZhcmlhYmxlIGZlYXR1cmVzIGFuZCAyMCBQQ0EgY29tcG9uZW50cyBmb3IgY2x1c3RlcmluZyBhbmQgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uKS4NCg0KRmluYWwgYW5ub3RhdGlvbnMgdG8gZXh0cmFjdCBjZWxsIHR5cGUgc2lnbmF0dXJlcyBpbXBvcnRlZCBmcm9tIG9yaWdpbmFsIGFuYWx5c2lzLg0KDQpgYGB7cn0NCmtyZW5lbE1ldGFkYXRhIDwtIHJlYWQuZGVsaW0oDQogIGZpbGUgPSBmaWxlLnBhdGgoZGF0YVBhdGgsIkdTRTEzMTgzNF9hbm5vdGF0aW9uX2JvbmVtYXJyb3cuY3N2IiksDQogIHNlcD0iLCIsIGhlYWRlciA9IFQpDQoNCmNvbG5hbWVzKGtyZW5lbE1ldGFkYXRhKSA8LSBjKCJjZWxsSWQiLCAiY2VsbF90eXBlIikNCg0Ka3JlbmVsTWV0YWRhdGEgPC0ga3JlbmVsTWV0YWRhdGEgJT4lDQogIG11dGF0ZShjZWxsSWQ9c3ViKCItIiwiLiIsY2VsbElkKSkNCnJvd25hbWVzKGtyZW5lbE1ldGFkYXRhKSA8LSBrcmVuZWxNZXRhZGF0YSRjZWxsSWQNCg0Ka3JlbmtlbENvdW50cyA8LSByZWFkLnRhYmxlKA0KICBmaWxlID0gZmlsZS5wYXRoKGRhdGFQYXRoLCJHU0UxMzE4MzRfQk1fTkRfV0RfV1QxNi50eHQiKSwNCiAgc2VwID0gIlx0IixoZWFkZXI9VCkNCiAgDQprcmVua2VsQ291bnRzIDwtIE1hdHJpeDo6TWF0cml4KGFzLm1hdHJpeChrcmVua2VsQ291bnRzKSkNCg0Ka3JlbmtlbFNldXJhdCA8LSBDcmVhdGVTZXVyYXRPYmplY3QoDQogICAga3JlbmtlbENvdW50c1sscm93bmFtZXMoa3JlbmVsTWV0YWRhdGEpXSwNCiAgICBwcm9qZWN0ID0gIktyZW5rZWwgZXQgYWwiLA0KICAgIGFzc2F5ID0gIlJOQSIsDQogICAgbWV0YS5kYXRhID0ga3JlbmVsTWV0YWRhdGENCiAgKQ0KICANCm5mZWF0dXJlcyA9IDIwMDANCm5wY3MgPC0gMjANCnJlc3MgPC0gYygwLjUpDQprcmVua2VsU2V1cmF0IDwtIGtyZW5rZWxTZXVyYXQgJT4lDQogIE5vcm1hbGl6ZURhdGEoDQogICAgdmVyYm9zZSA9IEYNCiAgKSAlPiUNCiAgRmluZFZhcmlhYmxlRmVhdHVyZXMoDQogICAgbmZlYXR1cmVzPW5mZWF0dXJlcywNCiAgICB2ZXJib3NlID0gRg0KICApICU+JQ0KICBTY2FsZURhdGEoDQogICAgdmVyYm9zZSA9IEYNCiAgKSAlPiUNCiAgUnVuUENBKA0KICAgIHZlcmJvc2UgPSBGDQogICkgJT4lDQogIEZpbmROZWlnaGJvcnMoDQogICAgcmVkdWN0aW9uID0gInBjYSIsDQogICAgZGltcyA9IHNlcShucGNzKSwNCiAgICBmb3JjZS5yZWNhbGM9VCwNCiAgICB2ZXJib3NlID0gRiwNCiAgKSAlPiUNCiAgRmluZENsdXN0ZXJzKA0KICAgIHJlc29sdXRpb249cmVzcywNCiAgICB2ZXJib3NlID0gRg0KICApICU+JQ0KICBSdW5VTUFQKA0KICAgIHJlZHVjdGlvbiA9ICJwY2EiLA0KICAgIGRpbXMgPSBzZXEobnBjcyksDQogICAgdmVyYm9zZSA9IEYNCiAgKSAlPiUNCiAgUnVuVFNORSgNCiAgICByZWR1Y3Rpb24gPSAicGNhIiwNCiAgICBkaW1zID0gc2VxKG5wY3MpLA0KICAgIHBlcnBsZXhpdHkgPSAxMDAsDQogICAgdmVyYm9zZSA9IEYNCiAgKQ0KDQpjbHVzdGVyaW5nIDwtICJSTkFfc25uX3Jlcy4wLjUiDQpJZGVudHMoa3JlbmtlbFNldXJhdCkgPC0gY2x1c3RlcmluZw0KYGBgDQoNCmBgYHtyfQ0KcDEgPC0gRGltUGxvdChrcmVua2VsU2V1cmF0LA0KICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidHNuZSIsDQogICAgICAgICAgICAgIGdyb3VwLmJ5ID0gIlJOQV9zbm5fcmVzLjAuNSIsDQogICAgICAgICAgICAgIGNvbHMgPSBjb2xvci5saXN0LA0KICAgICAgICAgICAgICBsYWJlbCA9IFQpDQpwMiA8LSBEaW1QbG90KGtyZW5rZWxTZXVyYXQsIA0KICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidHNuZSIsIA0KICAgICAgICAgICAgICBncm91cC5ieSA9ICJjZWxsX3R5cGUiLA0KICAgICAgICAgICAgICBjb2xzID0gY29sb3IubGlzdCwNCiAgICAgICAgICAgICAgbGFiZWwgPSBUKQ0KcDErcDINCmBgYA0KDQojIyMgR2V0IEtyZW5rZWwgQ2VsbCBUeXBlIE1hcmtlcnMNCg0KYGBge3J9DQpJZGVudHMoa3JlbmtlbFNldXJhdCkgPC0gImNlbGxfdHlwZSINCm1hcmtlcnNLcmVua2VsIDwtIGtyZW5rZWxTZXVyYXQgJT4lDQogIEZpbmRBbGxNYXJrZXJzKA0KICAgIGFzc2F5ID0gIlJOQSIsDQogICAgdGVzdC51c2UgPSAid2lsY294IiwNCiAgICBzbG90ID0gImRhdGEiLA0KICAgIG9ubHkucG9zID0gVCwNCiAgICB2ZXJib3NlID0gRikNCg0KdGFibGUobWFya2Vyc0tyZW5rZWxbbWFya2Vyc0tyZW5rZWwkcF92YWxfYWRqIDwgMC4wMSwiY2x1c3RlciJdKQ0KYGBgDQoNCiMjIyMgSGVhdE1hcCBvZiBUb3AgMTAwIEtyZW5rZWwgQ2VsbCBUeXBlIE1hcmtlcnMNCg0KYGBge3J9DQp0b3AxMDAgPC0gbWFya2Vyc0tyZW5rZWwgJT4lDQogIGZpbHRlcihwX3ZhbF9hZGogPCAwLjAxKSAlPiUNCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgICB0b3BfbihuID0gMTAwLCB3dCA9IGF2Z19sb2cyRkMpDQpgYGANCg0KYGBge3IsIGZpZy5hc3A9MS40fQ0Ka3JlbmtlbFNldXJhdCA8LSBTY2FsZURhdGEoa3JlbmtlbFNldXJhdCwNCiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSB1bmlxdWUoc29ydChjKFZhcmlhYmxlRmVhdHVyZXMoa3JlbmtlbFNldXJhdCksdG9wMTAwJGdlbmUpKSksDQogICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGKQ0KRG9IZWF0bWFwKGtyZW5rZWxTZXVyYXQsIGZlYXR1cmVzID0gdG9wMTAwJGdlbmUpICsgTm9MZWdlbmQoKQ0KYGBgDQoNCiMjIyMgVG9wIDIwIEtyZW5rZWwgTWFya2VycyBEb3RQbG90DQoNCmBgYHtyIGZpZy53aWR0aD0xNH0NCm50b3AgPC0gMTANCnB2YWwgPC0gMC4wMQ0KDQp0b3BNYXJrZXJzIDwtIG1hcmtlcnNLcmVua2VsICU+JQ0KICBmaWx0ZXIocF92YWxfYWRqIDwgcHZhbCkgJT4lDQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICB0b3BfbihudG9wLCBhdmdfbG9nMkZDKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBkcGx5cjo6c2VsZWN0KGdlbmUpICU+JQ0KICBkaXN0aW5jdCgpDQoNCkRvdFBsb3QoDQogIGtyZW5rZWxTZXVyYXQsDQogIGZlYXR1cmVzID0gcmV2KHRvcE1hcmtlcnMpDQogICkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LGhqdXN0ID0gMSkpDQpgYGANCg0KIyMjIFNlbGVjdCBDZWxsIFR5cGUgTW9kdWxlIGdlbmVzIGZyb20gbWFya2Vycw0KDQpHZW5lIG1vZHVsZXMgc2VsZWN0ZWQgZnJvbSB0aGUgdG9wIDIwMCBtYXJrZXIgZ2VuZXMgYXQgYW4gYWRqdXN0ZWQgcHZhbCA8IDAuMDEsIGEgbcOtbmltdW0gbG9nRkMgb2YgMC4yNSwgcHJlc2VudCBpbiBvdXIgU3Ryb21hbCBkYXRhc2V0IGFuZCBub3Qgc2hhcmVkIGJldHdlZW4gY2VsbCB0eXBlcy4NCg0KYGBge3J9DQpjZWxsdHlwZU1hcmtlcnMgPC0gbWFya2Vyc0tyZW5rZWwgJT4lDQogIGZpbHRlcihwX3ZhbF9hZGogPCAwLjAxKSAlPiUNCiAgZmlsdGVyKGF2Z19sb2cyRkMgPiAwLjI1KSAlPiUNCiAgZmlsdGVyKGdlbmUgJWluJSByb3duYW1lcyhieVNhbXBsZS5jY2EpKSAlPiUNCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIHRvcF9uKG4gPSAyMDAsIHd0ID0gYXZnX2xvZzJGQykgJT4lDQogIGZpbHRlcihnZW5lICVpbiUgcm93bmFtZXMoYnlTYW1wbGUuY2NhKSkgJT4lDQogIGRwbHlyOjpzZWxlY3QoY2x1c3RlcixnZW5lKQ0KDQp3aGljaER1cCA8LSB1bmlxdWUoc29ydChjZWxsdHlwZU1hcmtlcnMkZ2VuZVt3aGljaChkdXBsaWNhdGVkKGNlbGx0eXBlTWFya2VycyRnZW5lKSldKSkNCmNlbGx0eXBlTWFya2VycyA8LSBjZWxsdHlwZU1hcmtlcnMgJT4lIGZpbHRlcighZ2VuZSAlaW4lIHdoaWNoRHVwKQ0KDQp0YWJsZShjZWxsdHlwZU1hcmtlcnMkY2x1c3RlcikNCg0Ka3JlbmtlbFNldXJhdCA8LSBTY2FsZURhdGEoa3JlbmtlbFNldXJhdCwNCiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSB1bmlxdWUoc29ydChjKFZhcmlhYmxlRmVhdHVyZXMoa3JlbmtlbFNldXJhdCksY2VsbHR5cGVNYXJrZXJzJGdlbmUpKSksDQogICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGKQ0KRG9IZWF0bWFwKGtyZW5rZWxTZXVyYXQsIGZlYXR1cmVzID0gY2VsbHR5cGVNYXJrZXJzJGdlbmUpICsgTm9MZWdlbmQoKQ0KYGBgDQoNCmBgYHtyfQ0KbW9kdWxlR2VuZUxpc3RLcmVua2VsIDwtIGNlbGx0eXBlTWFya2VycyAlPiUNCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIGdyb3VwX3NwbGl0KCkNCm1vZHVsZUdlbmVMaXN0S3JlbmtlbCA8LSBhcy5saXN0KG1vZHVsZUdlbmVMaXN0S3JlbmtlbCkNCm5hbWVzKG1vZHVsZUdlbmVMaXN0S3JlbmtlbCkgPC0gbGV2ZWxzKGZhY3RvcihjZWxsdHlwZU1hcmtlcnMkY2x1c3RlcikpDQpgYGANCg0KIyMgR2V0IE5ldXRyb3BoaWwgTW9kdWxlcyBmcm9tIFhpZSBldCBhbCBwdWJsaWNhdGlvbg0KDQpEYXRhIG9idGFpbmVkIGZyb20gW1hpZSBldC4gYWwuIE5hdHVyZSAyMDIwXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTU5MC0wMjAtMDczNi16I2FydGljbGUtaW5mbykuIERhdGFzZXQgR1NFMTM3NTM5Lg0KDQpDbHVzdGVyaW5nIG9mIG5ldXRyb3BoaWxzIGZyb20gd3QgbmV1dHJvcGhpbCBzYW1wbGVzLiBDbHVzdGVyaW5nIG9mIHNhbXBsZXMgdXNpbmcgQ0NBIGZvciBpbnRlZ3JhdGlvbiBhbmQgYmF0Y2ggZWZmZWN0IGNvcnJlY3Rpb25zICgyMDAwIHZhcmlhYmxlIGZlYXR1cmVzLCAyMCBQQ0EgY29tcG9uZW50cykuDQoNCkZpbmFsIGFubm90YXRpb25zIHRvIGV4dHJhY3QgY2VsbCB0eXBlIHNpZ25hdHVyZXMgaW1wb3J0ZWQgZnJvbSBvcmlnaW5hbCBhbmFseXNpcy4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnhpZU1ldGFkYXRhIDwtIHJlYWQuZGVsaW0oDQogIGZpbGUgPSBmaWxlLnBhdGgoZGF0YVBhdGgsIkdTRTEzNzUzOV93dF9jdGxfbWV0YS50eHQiKSwNCiAgc2VwPSJcdCIsIGhlYWRlciA9IFQpDQp4aWVNZXRhZGF0YSA8LSB4aWVNZXRhZGF0YSAlPiUNCiAgbXV0YXRlKGNlbGxJZCA9IHJvd25hbWVzKC4pKSAlPiUNCiAgZHBseXI6OnNlbGVjdChjZWxsSWQsIG9yaWcuaWRlbnQsIGNlbGxfdHlwZSwgY2x1c3RlcikNCg0KbXlDb3VudHNGaWxlcyA8LSBkaXIoZmlsZS5wYXRoKGRhdGFQYXRoKSxwYXR0ZXJuID0gIkdTTS4rd3RfY3RsIikNCnhpZUNvdW50cyA8LSBsYXBwbHkobXlDb3VudHNGaWxlcywgZnVuY3Rpb24gKGYsZGF0YVBhdGgpIHsNCiAgc25hbWUgPC0gZ3N1YigiXyIsIi4iLGdzdWIoIkdTTVxcZCtfKHd0X2N0bF9cXHcrKS4rIiwiXFwxIixmKSkNCiAgY291bnRzIDwtIHJlYWQudGFibGUoDQogICAgZmlsZSA9IGd6ZmlsZShmaWxlLnBhdGgoZGF0YVBhdGgsZiksb3BlbiA9ICJyIiksDQogICAgc2VwID0gIiAiLGhlYWRlcj1UKQ0KICAgIGNvbG5hbWVzKGNvdW50cykgPC0gcGFzdGUwKHNuYW1lLCJfIixjb2xuYW1lcyhjb3VudHMpKQ0KICAgIGNvdW50cyA8LSBNYXRyaXg6Ok1hdHJpeChhcy5tYXRyaXgoY291bnRzKSkNCn0sZGF0YVBhdGgpIA0KDQp4aWVDb3VudHMgPC0gZG8uY2FsbChjYmluZCx4aWVDb3VudHMpDQp4aWVDb3VudHMgPC0geGllQ291bnRzWyxyb3duYW1lcyh4aWVNZXRhZGF0YSldDQp4aWVTZXVyYXQgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KA0KICAgIGNvdW50cyA9IHhpZUNvdW50cywNCiAgICBwcm9qZWN0ID0gIlhpZSBldCBhbCIsDQogICAgYXNzYXkgPSAiUk5BIiwNCiAgICBtZXRhLmRhdGEgPSB4aWVNZXRhZGF0YSwNCiAgICBuYW1lcy5kZWxpbSA9ICJfIiwNCiAgICBuYW1lcy5maWVsZCA9IDENCiAgKQ0KeGllU2V1cmF0IDwtIHhpZVNldXJhdFssIWlzLm5hKHhpZVNldXJhdCRjbHVzdGVyKV0NCg0KeGllU2V1cmF0TGlzdCA8LSBTcGxpdE9iamVjdCh4aWVTZXVyYXQsc3BsaXQuYnkgPSAib3JpZy5pZGVudCIpDQogIA0KeGllU2V1cmF0TGlzdCA8LSBsYXBwbHkoeGllU2V1cmF0TGlzdCwgZnVuY3Rpb24gKHgpIHsNCiAgICB4IDwtIHggJT4lIE5vcm1hbGl6ZURhdGEoDQogICAgICB2ZXJib3NlID0gRg0KICAgICkgJT4lDQogICAgICBGaW5kVmFyaWFibGVGZWF0dXJlcygNCiAgICAgICAgbmZlYXR1cmVzID0gbmZlYXR1cmVzLA0KICAgICAgICB2ZXJib3NlID0gRg0KICAgICAgICApICU+JQ0KICAgICAgU2NhbGVEYXRhKA0KICAgICAgICB2ZXJib3NlID0gRg0KICAgICAgKSAlPiUNCiAgICAgIFJ1blBDQSgNCiAgICAgICAgdmVyYm9zZSA9IEYNCiAgICAgICkNCiAgICByZXR1cm4oeCkNCiAgfSkNCiAgDQpjb21tb25GZWF0dXJlcyA8LSBTZWxlY3RJbnRlZ3JhdGlvbkZlYXR1cmVzKHhpZVNldXJhdExpc3QsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGKQ0KICANCnNtYW5jaG9ycyA8LSBGaW5kSW50ZWdyYXRpb25BbmNob3JzKA0KICB4aWVTZXVyYXRMaXN0LA0KICBhbmNob3IuZmVhdHVyZXMgPSBjb21tb25GZWF0dXJlcywNCiAgdmVyYm9zZSA9IEYNCiAgKQ0KICANCnhpZVNldXJhdC5jY2EgPC0gSW50ZWdyYXRlRGF0YShhbmNob3JzZXQgPSBzbWFuY2hvcnMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEYpDQogIA0KRGVmYXVsdEFzc2F5KHhpZVNldXJhdC5jY2EpIDwtICJpbnRlZ3JhdGVkIg0KICANCiMgUnVuIHRoZSBzdGFuZGFyZCB3b3JrZmxvdyBmb3IgdmlzdWFsaXphdGlvbiBhbmQgY2x1c3RlcmluZw0KbmZlYXR1cmVzIDwtIDIwMDANCm5wY3MgPC0gMjANCnhpZVNldXJhdC5jY2EgPC0geGllU2V1cmF0LmNjYSAlPiUNCiAgU2NhbGVEYXRhKA0KICAgIHZlcmJvc2UgPSBGDQogICkgJT4lDQogIFJ1blBDQSgNCiAgICBucGNzID0gNTAsDQogICAgdmVyYm9zZSA9IEYNCiAgKSAlPiUNCiAgRmluZE5laWdoYm9ycygNCiAgICByZWR1Y3Rpb24gPSAicGNhIiwNCiAgICBkaW1zID0gc2VxKG5wY3MpLA0KICAgIGZvcmNlLnJlY2FsYyA9IFQsDQogICAgdmVyYm9zZSA9IEYNCiAgKSAlPiUNCiAgRmluZENsdXN0ZXJzKA0KICAgIHJlc29sdXRpb24gPSByZXNzLA0KICAgIHZlcmJvc2UgPSBGDQogICkgJT4lDQogIFJ1blVNQVAoDQogICAgcmVkdWN0aW9uID0gInBjYSIsDQogICAgZGltcyA9IHNlcShucGNzKSwNCiAgICB2ZXJib3NlID0gRg0KICApICU+JQ0KICBSdW5UU05FKA0KICAgIHJlZHVjdGlvbiA9ICJwY2EiLA0KICAgIGRpbXMgPSBzZXEobnBjcyksDQogICAgcGVycGxleGl0eT0xMDAsDQogICAgdmVyYm9zZSA9IEYNCiAgKQ0KDQpjbHVzdGVyaW5nIDwtICJpbnRlZ3JhdGVkX3Nubl9yZXMuMC41Ig0KSWRlbnRzKHhpZVNldXJhdC5jY2EpIDwtIGNsdXN0ZXJpbmcNCmBgYA0KDQpgYGB7cn0NCnAxIDwtIERpbVBsb3QoeGllU2V1cmF0LmNjYSwNCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInRzbmUiLA0KICAgICAgICAgICAgICBncm91cC5ieSA9IGNsdXN0ZXJpbmcsDQogICAgICAgICAgICAgIGNvbHMgPSBjb2xvci5saXN0LA0KICAgICAgICAgICAgICBsYWJlbCA9IFQpDQpwMiA8LSBEaW1QbG90KHhpZVNldXJhdC5jY2EsIA0KICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidHNuZSIsIA0KICAgICAgICAgICAgICBncm91cC5ieSA9ICJjZWxsX3R5cGUiLA0KICAgICAgICAgICAgICBjb2xzID0gY29sb3IubGlzdCwNCiAgICAgICAgICAgICAgbGFiZWwgPSBUKQ0KcDErcDINCmBgYA0KDQpgYGB7cn0NCnAxIDwtIERpbVBsb3QoeGllU2V1cmF0LmNjYSwNCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInRzbmUiLA0KICAgICAgICAgICAgICBncm91cC5ieSA9IGNsdXN0ZXJpbmcsDQogICAgICAgICAgICAgIGNvbHMgPSBjb2xvci5saXN0LA0KICAgICAgICAgICAgICBsYWJlbCA9IFQpDQpwMiA8LSBEaW1QbG90KHhpZVNldXJhdC5jY2EsIA0KICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidHNuZSIsIA0KICAgICAgICAgICAgICBncm91cC5ieSA9ICJjbHVzdGVyIiwNCiAgICAgICAgICAgICAgY29scyA9IGNvbG9yLmxpc3QsDQogICAgICAgICAgICAgIGxhYmVsID0gVCkNCnAxK3AyDQpgYGANCg0KIyMjIFN1YnNldCBYaWUgdG8gQm9uZSBNYXJyb3cgZGVyaXZlZCBOZXV0cm9waGlscw0KDQpTZWxlY3Qgc2FtcGxlcyBmcm9tIGJvbmUgbWFycm93IGFuZCByZW1vdmUgdGhvc2UgbGVmdCBhdCBjbHVzdGVycyBHNSB3aGljaCBjbGVhcmx5IGNvbWVzIGZyb20gU1Agb3IgUEIuDQoNCmBgYHtyfQ0KeGllU2V1cmF0LmNjYSA8LSBzdWJzZXQoeGllU2V1cmF0LmNjYSwgb3JpZy5pZGVudCAlaW4lIGMoInd0LmN0bC5ibTEiLCJ3dC5jdGwuYm0yIikgJiBjbHVzdGVyICVpbiUgYygiRzAiLCJHMSIsIkcyIiwiRzMiLCJHNCIsIkdNIikpDQpgYGANCg0KYGBge3J9DQpwMSA8LSBEaW1QbG90KHhpZVNldXJhdC5jY2EsDQogICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ0c25lIiwNCiAgICAgICAgICAgICAgZ3JvdXAuYnkgPSBjbHVzdGVyaW5nLA0KICAgICAgICAgICAgICBjb2xzID0gY29sb3IubGlzdCwNCiAgICAgICAgICAgICAgbGFiZWwgPSBUKQ0KcDIgPC0gRGltUGxvdCh4aWVTZXVyYXQuY2NhLCANCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInRzbmUiLCANCiAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3RlciIsDQogICAgICAgICAgICAgIGNvbHMgPSBjb2xvci5saXN0LA0KICAgICAgICAgICAgICBsYWJlbCA9IFQpDQpwMStwMg0KYGBgDQoNCiMjIyBHZXQgWGllIENlbGwgVHlwZSBNYXJrZXJzDQoNCmBgYHtyfQ0KRGVmYXVsdEFzc2F5KHhpZVNldXJhdC5jY2EpIDwtICJSTkEiDQpJZGVudHMoeGllU2V1cmF0LmNjYSkgPC0gImNsdXN0ZXIiDQptYXJrZXJzWGllIDwtIHhpZVNldXJhdC5jY2EgJT4lDQogIEZpbmRBbGxNYXJrZXJzKA0KICAgIGFzc2F5ID0gIlJOQSIsDQogICAgdGVzdC51c2UgPSAid2lsY294IiwNCiAgICBzbG90ID0gImRhdGEiLA0KICAgIG9ubHkucG9zID0gVCwNCiAgICB2ZXJib3NlID0gRikNCg0KdGFibGUobWFya2Vyc1hpZVttYXJrZXJzWGllJHBfdmFsX2FkaiA8IDAuMDEsImNsdXN0ZXIiXSkNCmBgYA0KDQojIyMjIEhlYXRNYXAgb2YgVG9wIDEwMCBLcmVua2VsIENlbGwgVHlwZSBNYXJrZXJzDQoNCmBgYHtyfQ0KdG9wMTAwIDwtIG1hcmtlcnNYaWUgJT4lDQogIGZpbHRlcihwX3ZhbF9hZGogPCAwLjAxKSAlPiUNCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgICB0b3BfbihuID0gMTAwLCB3dCA9IGF2Z19sb2cyRkMpDQpgYGANCg0KYGBge3IsIGZpZy5hc3A9MS40fQ0KeGllU2V1cmF0LmNjYSA8LSBTY2FsZURhdGEoeGllU2V1cmF0LmNjYSwNCiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSB1bmlxdWUoc29ydChjKFZhcmlhYmxlRmVhdHVyZXMoeGllU2V1cmF0LmNjYSksdG9wMTAwJGdlbmUpKSksDQogICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGKQ0KRG9IZWF0bWFwKHhpZVNldXJhdC5jY2EsIGZlYXR1cmVzID0gdG9wMTAwJGdlbmUpICsgTm9MZWdlbmQoKQ0KYGBgDQoNCiMjIyMgVG9wIDIwIEtyZW5rZWwgTWFya2VycyBEb3RQbG90DQoNCmBgYHtyIGZpZy53aWR0aD0xNH0NCm50b3AgPC0gMTANCnB2YWwgPC0gMC4wMQ0KDQp0b3BNYXJrZXJzIDwtIG1hcmtlcnNYaWUgJT4lDQogIGZpbHRlcihwX3ZhbF9hZGogPCBwdmFsKSAlPiUNCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIHRvcF9uKG50b3AsIGF2Z19sb2cyRkMpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGRwbHlyOjpzZWxlY3QoZ2VuZSkgJT4lDQogIGRpc3RpbmN0KCkNCg0KRG90UGxvdCgNCiAgeGllU2V1cmF0LmNjYSwNCiAgZmVhdHVyZXMgPSByZXYodG9wTWFya2VycykNCiAgKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsaGp1c3QgPSAxKSkNCmBgYA0KDQojIyMgU2VsZWN0IENlbGwgVHlwZSBNb2R1bGUgZ2VuZXMgZnJvbSBtYXJrZXJzDQoNCkdlbmUgbW9kdWxlcyBzZWxlY3RlZCBmcm9tIHRoZSB0b3AgMjAwIG1hcmtlciBnZW5lcyBhdCBhbiBhZGp1c3RlZCBwdmFsIDwgMC4wMSwgYSBtw61uaW11bSBsb2dGQyBvZiAwLjI1LCBwcmVzZW50IGluIG91ciBTdHJvbWFsIGRhdGFzZXQgYW5kIG5vdCBzaGFyZWQgYmV0d2VlbiBjZWxsIHR5cGVzLg0KDQpgYGB7cn0NCmNlbGx0eXBlTWFya2VycyA8LSBtYXJrZXJzWGllICU+JQ0KICBmaWx0ZXIocF92YWxfYWRqIDwgMC4wMSkgJT4lDQogIGZpbHRlcihhdmdfbG9nMkZDID4gMC4yNSkgJT4lDQogIGZpbHRlcihnZW5lICVpbiUgcm93bmFtZXMoYnlTYW1wbGUuY2NhKSkgJT4lDQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICB0b3BfbihuID0gMTAwLCB3dCA9IGF2Z19sb2cyRkMpICU+JQ0KICAjIGZpbHRlcihnZW5lICVpbiUgcm93bmFtZXMoYnlTYW1wbGUuY2NhKSkgJT4lDQogIGRwbHlyOjpzZWxlY3QoY2x1c3RlcixnZW5lKSAlPiUNCiAgZmlsdGVyKGNsdXN0ZXIgIT0gIkdNIikNCg0Kd2hpY2hEdXAgPC0gdW5pcXVlKHNvcnQoY2VsbHR5cGVNYXJrZXJzJGdlbmVbd2hpY2goZHVwbGljYXRlZChjZWxsdHlwZU1hcmtlcnMkZ2VuZSkpXSkpDQpjZWxsdHlwZU1hcmtlcnMgPC0gY2VsbHR5cGVNYXJrZXJzICU+JSBmaWx0ZXIoIWdlbmUgJWluJSB3aGljaER1cCkNCg0KdGFibGUoY2VsbHR5cGVNYXJrZXJzJGNsdXN0ZXIpDQoNCnhpZVNldXJhdC5jY2EgPC0gU2NhbGVEYXRhKHhpZVNldXJhdC5jY2EsDQogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gdW5pcXVlKHNvcnQoYyhWYXJpYWJsZUZlYXR1cmVzKHhpZVNldXJhdC5jY2EpLGNlbGx0eXBlTWFya2VycyRnZW5lKSkpLA0KICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRikNCkRvSGVhdG1hcCh4aWVTZXVyYXQuY2NhLCBmZWF0dXJlcyA9IGNlbGx0eXBlTWFya2VycyRnZW5lKSArIE5vTGVnZW5kKCkNCmBgYA0KDQpgYGB7cn0NCm1vZHVsZUdlbmVMaXN0WGllIDwtIGNlbGx0eXBlTWFya2VycyAlPiUNCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIGdyb3VwX3NwbGl0KCkNCm1vZHVsZUdlbmVMaXN0WGllIDwtIGFzLmxpc3QobW9kdWxlR2VuZUxpc3RYaWUpDQpuYW1lcyhtb2R1bGVHZW5lTGlzdFhpZSkgPC0gbGV2ZWxzKGZhY3RvcihjZWxsdHlwZU1hcmtlcnMkY2x1c3RlcikpDQpgYGANCg0KIyMgQ2VsbCBUeXBlIE1vZHVsZSBTY29yZXMNCg0KYGBge3J9DQptb2R1bGVHZW5lTGlzdCA8LSBjKG1vZHVsZUdlbmVMaXN0S3JlbmtlbCxtb2R1bGVHZW5lTGlzdFhpZSkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZm9yIChpIGluIG5hbWVzKG1vZHVsZUdlbmVMaXN0KSkgew0KICBieVNhbXBsZS5jY2EgPC0gQWRkTW9kdWxlU2NvcmUoYnlTYW1wbGUuY2NhDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICxmZWF0dXJlcyA9IGxpc3QoaT1tb2R1bGVHZW5lTGlzdFtbaV1dJGdlbmUpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICxuYW1lID0gIHBhc3RlMChpLCIuUk5BTW9kdWxlIikNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLGFzc2F5ID0gIlJOQSINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLHZlcmJvc2UgPSBGDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCn0NCmBgYA0KDQojIyMgS3JlbmtlbCBDZWxsIFR5cGUgTW9kdWxlIFNjb3JlcyBQbG90cw0KDQpgYGB7cn0NCmRmIDwtIEVtYmVkZGluZ3MoYnlTYW1wbGUuY2NhLHJlZHVjdGlvbiA9ICJ0c25lIikNCmRpbXMgPC0gY29sbmFtZXMoZGYpDQpkZiA8LSBjYmluZChkZixieVNhbXBsZS5jY2FAbWV0YS5kYXRhKQ0KYGBgDQoNCmBgYHtyfQ0KZGYgJT4lDQogIGZpbHRlcihTYW1wbGVOYW1lID09ICJDRDExYl9XVCIgJiAhTWFudWFsQ2x1c3RlcmluZyAlaW4lIGMoIkMwIiwiQzEiLCJDMiIsIkMzIiwiQzUiLCJDNiIsIkM4IiwiQzkiLCJDOWIiLCJDMTAiKSkgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gY29udGFpbnMoIlgiKSwgbmFtZXNfdG8gPSAiTW9kdWxlIiwgdmFsdWVzX3RvID0gIlNjb3JlIikgJT4lDQogIG11dGF0ZShNb2R1bGU9c3ViKCIuUk5BTW9kdWxlMSIsIiIsTW9kdWxlKSkgJT4lDQogIGdncGxvdChhZXNfc3RyaW5nKHg9ZGltc1sxXSx5PWRpbXNbMl0sY29sb3I9IlNjb3JlIikpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZ3JleSIsIm9yYW5nZSIsInJlZCIpKSgzKSxuYW1lPSJMb2coTm9ybUNvdW50cykiKSArDQogIGZhY2V0X3dyYXAoIk1vZHVsZSIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KIyMjIFhpZSBDZWxsIFR5cGUgTW9kdWxlIFNjb3JlcyBQbG90cw0KDQpgYGB7cn0NCmRmIDwtIEVtYmVkZGluZ3MoYnlTYW1wbGUuY2NhLHJlZHVjdGlvbiA9ICJ0c25lIikNCmRpbXMgPC0gY29sbmFtZXMoZGYpDQpkZiA8LSBjYmluZChkZixieVNhbXBsZS5jY2FAbWV0YS5kYXRhKQ0KYGBgDQoNCmBgYHtyfQ0KZGYgJT4lDQogIGZpbHRlcihTYW1wbGVOYW1lID09ICJDRDExYl9XVCIgJiBNYW51YWxDbHVzdGVyaW5nICVpbiUgYygiQzAiLCJDMSIsIkMyIiwiQzMiLCJDNSIsIkM2IiwiQzgiLCJDOSIsIkM5YiIsIkMxMCIpKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjb250YWlucyhjKCJHMCIsIkcxIiwiRzIiLCJHMyIsIkc0IikpLCBuYW1lc190byA9ICJNb2R1bGUiLCB2YWx1ZXNfdG8gPSAiU2NvcmUiKSAlPiUNCiAgbXV0YXRlKE1vZHVsZT1zdWIoIi5STkFNb2R1bGUxIiwiIixNb2R1bGUpKSAlPiUNCiAgZ2dwbG90KGFlc19zdHJpbmcoeD1kaW1zWzFdLHk9ZGltc1syXSxjb2xvcj0iU2NvcmUiKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gY29sb3JSYW1wUGFsZXR0ZShjKCJncmV5Iiwib3JhbmdlIiwicmVkIikpKDMpLG5hbWU9IkxvZyhOb3JtQ291bnRzKSIpICsNCiAgZmFjZXRfd3JhcCgiTW9kdWxlIikgKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQojIyMgTW9kdWxlIEFzaW5nbWVudHMgdG8gQ2x1c3RlcnMNCg0KQ2x1c3RlciBhc3NpZ25lZCB0byB0aGUgbWF4aW11bSBlbnJpY2htZW50IG1vZHVsZSBzY29yZSArLSAwLjAyLiBTb21lIGNsdXN0ZXJzIG1heSBzY29yZSB0d28gbW9kdWxlcyBvciBtb3JlIGFzIG1heGltdW0uDQoNCkFuYWx5c2lzIGJhc2VkIG9uIFdUIHRyYW5zY3JpcHRvbWUgb25seS4NCg0KIyMjIE1vbm9jeXRlIEtyZW5rZWwgTW9kdWxlcw0KDQpgYGB7cn0NCmNsdXN0ZXJpbmcgPC0gIk1hbnVhbENsdXN0ZXJpbmciDQpieVNhbXBsZS5jY2FAbWV0YS5kYXRhICU+JQ0KICBmaWx0ZXIoU2FtcGxlTmFtZSA9PSAiQ0QxMWJfV1QiICYgIU1hbnVhbENsdXN0ZXJpbmcgJWluJSBjKCJDMCIsIkMxIiwiQzIiLCJDMyIsIkM1IiwiQzYiLCJDOCIsIkM5IiwiQzliIiwiQzEwIikpICU+JQ0KICBkcGx5cjo6c2VsZWN0KGNvbnRhaW5zKGMoY2x1c3RlcmluZywiTW9kdWxlMSIpKSkgJT4lDQogIGRwbHlyOjpzZWxlY3QoLWNvbnRhaW5zKGMoIkcwIiwiRzEiLCJHMiIsIkczIiwiRzQiKSkpICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IGNvbnRhaW5zKCJSTkFNb2R1bGUxIiksbmFtZXNfdG8gPSAiTW9kdWxlIix2YWx1ZXNfdG8gPSAiU2NvcmUiKSAlPiUNCiAgcmVuYW1lKENsdXN0ZXI9Y2x1c3RlcmluZykgJT4lDQogIGdyb3VwX2J5KE1vZHVsZSkgJT4lDQogIHN1bW1hcmlzZShTY29yZT1zY2FsZShTY29yZSxjZW50ZXIgPSBULHNjYWxlID0gVCksQ2x1c3Rlcj1DbHVzdGVyKSAlPiUNCiAgZ3JvdXBfYnkoQ2x1c3RlcixNb2R1bGUpICU+JQ0KICBzdW1tYXJpc2UoQXZlcmFnZVNjb3JlPW1lYW4oU2NvcmUpKSAlPiUNCiAgZ3JvdXBfYnkoQ2x1c3RlcikgJT4lDQogIHN1bW1hcmlzZShNb2R1bGU9TW9kdWxlLEF2ZXJhZ2VTY29yZVNjYWxlZD1zY2FsZShBdmVyYWdlU2NvcmUpLE1heEVucmljaG1lbnQ9KEF2ZXJhZ2VTY29yZSA+PSAobWF4KEF2ZXJhZ2VTY29yZSktMC4wMikpKSAlPiUNCiAgbXV0YXRlKE1vZHVsZT1zdWIoIi5STkFNb2R1bGUxIiwiIixNb2R1bGUpKSAlPiUNCiAgbXV0YXRlKHBsb3RCb3JkZXI9aWZlbHNlKE1heEVucmljaG1lbnQsMS41LDApKSAlPiUNCiAgZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50KGFlc19zdHJpbmcoeD0iTW9kdWxlIix5PSJDbHVzdGVyIixmaWxsPSJBdmVyYWdlU2NvcmVTY2FsZWQiLGNvbG9yPSJNYXhFbnJpY2htZW50Iiwgc3Ryb2tlID0gInBsb3RCb3JkZXIiKSxzaXplPTYsc2hhcGU9MjEpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiZ3JleTkwIixoaWdoID0gImJsdWUiKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKE5BLCJyZWQiKSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LGhqdXN0ID0gMSkpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgQXNzaWduIFNvbW1lcmthbXAgY2VsbCB0eXBlIHRvIGNsdXN0ZXJzIHVzaW5nIFdUIGluZm8NCmNsdXN0ZXJpbmcgPC0gIk1hbnVhbENsdXN0ZXJpbmciDQpjZWxsVHlwZUFubm90IDwtIGJ5U2FtcGxlLmNjYUBtZXRhLmRhdGEgJT4lDQogIGZpbHRlcihTYW1wbGVOYW1lID09ICJDRDExYl9XVCIgJiAhTWFudWFsQ2x1c3RlcmluZyAlaW4lIGMoIkMwIiwiQzEiLCJDMiIsIkMzIiwiQzUiLCJDNiIsIkM4IiwiQzkiLCJDOWIiLCJDMTAiKSkgJT4lDQogIGRwbHlyOjpzZWxlY3QoY29udGFpbnMoYyhjbHVzdGVyaW5nLCJNb2R1bGUxIikpKSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtY29udGFpbnMoYygiRzAiLCJHMSIsIkcyIiwiRzMiLCJHNCIpKSkgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gY29udGFpbnMoIlJOQU1vZHVsZTEiKSxuYW1lc190byA9ICJNb2R1bGUiLHZhbHVlc190byA9ICJTY29yZSIpICU+JQ0KICByZW5hbWUoQ2x1c3Rlcj1jbHVzdGVyaW5nKSAlPiUNCiAgZ3JvdXBfYnkoTW9kdWxlKSAlPiUNCiAgc3VtbWFyaXNlKFNjb3JlPXNjYWxlKFNjb3JlLGNlbnRlciA9IFQsc2NhbGUgPSBUKSxDbHVzdGVyPUNsdXN0ZXIpICU+JQ0KICBncm91cF9ieShDbHVzdGVyLE1vZHVsZSkgJT4lDQogIHN1bW1hcmlzZShBdmVyYWdlU2NvcmU9bWVhbihTY29yZSkpICU+JQ0KICBncm91cF9ieShDbHVzdGVyKSAlPiUNCiAgc3VtbWFyaXNlKE1vZHVsZT1Nb2R1bGUsQXZlcmFnZVNjb3JlU2NhbGVkPXNjYWxlKEF2ZXJhZ2VTY29yZSksTWF4RW5yaWNobWVudD0oQXZlcmFnZVNjb3JlID49IChtYXgoQXZlcmFnZVNjb3JlKS0wLjAyKSkpICU+JQ0KICBtdXRhdGUoTW9kdWxlPXN1YigiLlJOQU1vZHVsZTEiLCIiLE1vZHVsZSkpICU+JQ0KICBtdXRhdGUoU2VsZWN0ZWQ9aWZlbHNlKE1heEVucmljaG1lbnQsMSwwKSkgJT4lDQogIGZpbHRlcihTZWxlY3RlZCA9PSAxKSAlPiUNCiAgZ3JvdXBfYnkoQ2x1c3RlcikgJT4lDQogIHN1bW1hcmlzZShDZWxsVHlwZTEgPSBwYXN0ZShNb2R1bGUsY29sbGFwc2U9Il8iKSkNCg0KY2VsbFR5cGVBbm5vdCA8LSBjZWxsVHlwZUFubm90ICU+JQ0KICBtdXRhdGUoQ2VsbFR5cGUxID0gc3ViKCJcXC4iLCJfIixzdWIoIl5YXFxkK1xcLiIsIiIsQ2VsbFR5cGUxLHBlcmw9VCkpKQ0KYGBgDQoNCmBgYHtyfQ0KYnlTYW1wbGUuY2NhQG1ldGEuZGF0YSA8LSBieVNhbXBsZS5jY2FAbWV0YS5kYXRhICU+JQ0KICBsZWZ0X2pvaW4oY2VsbFR5cGVBbm5vdCwgYnkgPSBjKCJNYW51YWxDbHVzdGVyaW5nIiA9ICJDbHVzdGVyIikpICU+JSBhcy5kYXRhLmZyYW1lKCkNCnJvd25hbWVzKGJ5U2FtcGxlLmNjYUBtZXRhLmRhdGEpID0gY29sbmFtZXMoYnlTYW1wbGUuY2NhKQ0KYGBgDQoNCmBgYHtyfQ0KcDEgPC0gRGltUGxvdChieVNhbXBsZS5jY2EsDQogICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ0c25lIiwNCiAgICAgICAgICAgICAgZ3JvdXAuYnkgPSBjbHVzdGVyaW5nLA0KICAgICAgICAgICAgICBjb2xzID0gY29sb3IubGlzdCwNCiAgICAgICAgICAgICAgbGFiZWwgPSBUKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCnAyIDwtIERpbVBsb3QoYnlTYW1wbGUuY2NhLCANCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInRzbmUiLCANCiAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiQ2VsbFR5cGUxIiwNCiAgICAgICAgICAgICAgY29scyA9IGNvbG9yLmxpc3QsDQogICAgICAgICAgICAgIGxhYmVsID0gRikNCnAxK3AyDQpgYGANCg0KIyMjIE5ldXRyb3BoaWxzIFhpZSBNb2R1bGVzDQoNCmBgYHtyfQ0KY2x1c3RlcmluZyA8LSAiTWFudWFsQ2x1c3RlcmluZyINCmJ5U2FtcGxlLmNjYUBtZXRhLmRhdGEgJT4lDQogIGZpbHRlcihTYW1wbGVOYW1lID09ICJDRDExYl9XVCIgJiBNYW51YWxDbHVzdGVyaW5nICVpbiUgYygiQzAiLCJDMSIsIkMyIiwiQzMiLCJDNSIsIkM2IiwiQzgiLCJDOSIsIkM5YiIsIkMxMCIpKSAlPiUNCiAgZHBseXI6OnNlbGVjdChjb250YWlucyhjKGNsdXN0ZXJpbmcsIkcwIiwiRzEiLCJHMiIsIkczIiwiRzQiKSkpICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IGNvbnRhaW5zKCJSTkFNb2R1bGUxIiksbmFtZXNfdG8gPSAiTW9kdWxlIix2YWx1ZXNfdG8gPSAiU2NvcmUiKSAlPiUNCiAgcmVuYW1lKENsdXN0ZXI9Y2x1c3RlcmluZykgJT4lDQogIGdyb3VwX2J5KE1vZHVsZSkgJT4lDQogIHN1bW1hcmlzZShTY29yZT1zY2FsZShTY29yZSxjZW50ZXIgPSBULHNjYWxlID0gVCksQ2x1c3Rlcj1DbHVzdGVyKSAlPiUNCiAgZ3JvdXBfYnkoQ2x1c3RlcixNb2R1bGUpICU+JQ0KICBzdW1tYXJpc2UoQXZlcmFnZVNjb3JlPW1lYW4oU2NvcmUpKSAlPiUNCiAgZ3JvdXBfYnkoQ2x1c3RlcikgJT4lDQogIHN1bW1hcmlzZShNb2R1bGU9TW9kdWxlLEF2ZXJhZ2VTY29yZVNjYWxlZD1zY2FsZShBdmVyYWdlU2NvcmUpLE1heEVucmljaG1lbnQ9KEF2ZXJhZ2VTY29yZSA+PSAobWF4KEF2ZXJhZ2VTY29yZSktMC4wMikpKSAlPiUNCiAgbXV0YXRlKE1vZHVsZT1zdWIoIi5STkFNb2R1bGUxIiwiIixNb2R1bGUpKSAlPiUNCiAgbXV0YXRlKHBsb3RCb3JkZXI9aWZlbHNlKE1heEVucmljaG1lbnQsMS41LDApKSAlPiUNCiAgZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50KGFlc19zdHJpbmcoeD0iTW9kdWxlIix5PSJDbHVzdGVyIixmaWxsPSJBdmVyYWdlU2NvcmVTY2FsZWQiLGNvbG9yPSJNYXhFbnJpY2htZW50Iiwgc3Ryb2tlID0gInBsb3RCb3JkZXIiKSxzaXplPTYsc2hhcGU9MjEpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiZ3JleTkwIixoaWdoID0gImJsdWUiKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKE5BLCJyZWQiKSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1KSkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBBc3NpZ24gU29tbWVya2FtcCBjZWxsIHR5cGUgdG8gY2x1c3RlcnMgdXNpbmcgV1QgaW5mbw0KY2x1c3RlcmluZyA8LSAiTWFudWFsQ2x1c3RlcmluZyINCmNlbGxUeXBlQW5ub3QgPC0gYnlTYW1wbGUuY2NhQG1ldGEuZGF0YSAlPiUNCiAgZmlsdGVyKFNhbXBsZU5hbWUgPT0gIkNEMTFiX1dUIiAmIE1hbnVhbENsdXN0ZXJpbmcgJWluJSBjKCJDMCIsIkMxIiwiQzIiLCJDMyIsIkM1IiwiQzYiLCJDOCIsIkM5IiwiQzliIiwiQzEwIikpICU+JQ0KICBkcGx5cjo6c2VsZWN0KGNvbnRhaW5zKGMoY2x1c3RlcmluZywiRzAiLCJHMSIsIkcyIiwiRzMiLCJHNCIpKSkgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gY29udGFpbnMoIlJOQU1vZHVsZTEiKSxuYW1lc190byA9ICJNb2R1bGUiLHZhbHVlc190byA9ICJTY29yZSIpICU+JQ0KICByZW5hbWUoQ2x1c3Rlcj1jbHVzdGVyaW5nKSAlPiUNCiAgZ3JvdXBfYnkoTW9kdWxlKSAlPiUNCiAgc3VtbWFyaXNlKFNjb3JlPXNjYWxlKFNjb3JlLGNlbnRlciA9IFQsc2NhbGUgPSBUKSxDbHVzdGVyPUNsdXN0ZXIpICU+JQ0KICBncm91cF9ieShDbHVzdGVyLE1vZHVsZSkgJT4lDQogIHN1bW1hcmlzZShBdmVyYWdlU2NvcmU9bWVhbihTY29yZSkpICU+JQ0KICBncm91cF9ieShDbHVzdGVyKSAlPiUNCiAgc3VtbWFyaXNlKE1vZHVsZT1Nb2R1bGUsQXZlcmFnZVNjb3JlU2NhbGVkPXNjYWxlKEF2ZXJhZ2VTY29yZSksTWF4RW5yaWNobWVudD0oQXZlcmFnZVNjb3JlID49IChtYXgoQXZlcmFnZVNjb3JlKS0wLjAyKSkpICU+JQ0KICBtdXRhdGUoTW9kdWxlPXN1YigiLlJOQU1vZHVsZTEiLCIiLE1vZHVsZSkpICU+JQ0KICBtdXRhdGUoU2VsZWN0ZWQ9aWZlbHNlKE1heEVucmljaG1lbnQsMSwwKSkgJT4lDQogIGZpbHRlcihTZWxlY3RlZCA9PSAxKSAlPiUNCiAgZ3JvdXBfYnkoQ2x1c3RlcikgJT4lDQogIHN1bW1hcmlzZShDZWxsVHlwZTIgPSBwYXN0ZShNb2R1bGUsY29sbGFwc2U9Il8iKSkNCmBgYA0KDQpgYGB7cn0NCmJ5U2FtcGxlLmNjYUBtZXRhLmRhdGEgPC0gYnlTYW1wbGUuY2NhQG1ldGEuZGF0YSAlPiUNCiAgbGVmdF9qb2luKGNlbGxUeXBlQW5ub3QsIGJ5ID0gYygiTWFudWFsQ2x1c3RlcmluZyIgPSAiQ2x1c3RlciIpKSAlPiUgYXMuZGF0YS5mcmFtZSgpDQpyb3duYW1lcyhieVNhbXBsZS5jY2FAbWV0YS5kYXRhKSA9IGNvbG5hbWVzKGJ5U2FtcGxlLmNjYSkNCmBgYA0KDQpgYGB7cn0NCnAxIDwtIERpbVBsb3QoYnlTYW1wbGUuY2NhLA0KICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidHNuZSIsDQogICAgICAgICAgICAgIGdyb3VwLmJ5ID0gY2x1c3RlcmluZywNCiAgICAgICAgICAgICAgY29scyA9IGNvbG9yLmxpc3QsDQogICAgICAgICAgICAgIGxhYmVsID0gVCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpwMiA8LSBEaW1QbG90KGJ5U2FtcGxlLmNjYSwgDQogICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ0c25lIiwgDQogICAgICAgICAgICAgIGdyb3VwLmJ5ID0gIkNlbGxUeXBlMiIsDQogICAgICAgICAgICAgIGNvbHMgPSBjb2xvci5saXN0LA0KICAgICAgICAgICAgICBsYWJlbCA9IEYpDQpwMStwMg0KYGBgDQoNCg0KYGBge3J9DQpieVNhbXBsZS5jY2FAbWV0YS5kYXRhIDwtIGJ5U2FtcGxlLmNjYUBtZXRhLmRhdGEgICU+JQ0KICBtdXRhdGUoQ2VsbFR5cGUxID0gaWZlbHNlKGlzLm5hKENlbGxUeXBlMSksQ2VsbFR5cGUyLENlbGxUeXBlMSkpICU+JSBkcGx5cjo6c2VsZWN0KC1DZWxsVHlwZTIpDQpgYGANCg0KDQojIyMgQ2VsbCBUeXBlIEJ5IENsdXN0ZXINCg0KYGBge3J9DQpwMSA8LSBEaW1QbG90KGJ5U2FtcGxlLmNjYSwNCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInRzbmUiLA0KICAgICAgICAgICAgICBncm91cC5ieSA9IGNsdXN0ZXJpbmcsDQogICAgICAgICAgICAgIGNvbHMgPSBjb2xvci5saXN0LA0KICAgICAgICAgICAgICBsYWJlbCA9IFQpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KcDIgPC0gRGltUGxvdChieVNhbXBsZS5jY2EsIA0KICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidHNuZSIsIA0KICAgICAgICAgICAgICBncm91cC5ieSA9ICJDZWxsVHlwZTEiLA0KICAgICAgICAgICAgICBjb2xzID0gY29sb3IubGlzdCwNCiAgICAgICAgICAgICAgbGFiZWwgPSBGKQ0KcDErcDINCmBgYA0KDQojIyMgQ2VsbCBUeXBlIEJ5IENvbmRpdGlvbg0KDQpgYGB7cn0NCkRpbVBsb3QoYnlTYW1wbGUuY2NhLA0KICAgICAgICByZWR1Y3Rpb24gPSAidHNuZSIsDQogICAgICAgIGdyb3VwLmJ5ID0gIkNlbGxUeXBlMSIsDQogICAgICAgIGNvbHMgPSBjb2xvci5saXN0LA0KICAgICAgICBzcGxpdC5ieSA9ICJTYW1wbGVOYW1lIikNCmBgYA0KDQoNCiMjIyBDZWxsIFR5cGUgUHJvcG9ydGlvbnMNCg0KYGBge3J9DQpieVNhbXBsZS5jY2FAbWV0YS5kYXRhICU+JQ0KICBnZ3Bsb3QoYWVzKHg9IiIsZmlsbD1DZWxsVHlwZTEpKSArDQogIGdlb21fYmFyKHN0YXQ9ImNvdW50Iixwb3NpdGlvbj0iZmlsbCIpICsNCiAgY29vcmRfcG9sYXIoInkiLHN0YXJ0ID0gMCkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvci5saXN0KSArDQogIGZhY2V0X3dyYXAoIlNhbXBsZU5hbWUiKSArDQogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQ0KYGBgDQoNCmBgYHtyfQ0KYnlTYW1wbGUuY2NhQG1ldGEuZGF0YSAlPiUNCiAgZ3JvdXBfYnkoU2FtcGxlTmFtZSkgJT4lDQogIGNvdW50KA0KICAgIENlbGxUeXBlMQ0KICApICU+JQ0KICBwaXZvdF93aWRlcihpZF9jb2xzID0gU2FtcGxlTmFtZSwgbmFtZXNfZnJvbSA9IENlbGxUeXBlMSwgdmFsdWVzX2Zyb20gPSBuKQ0KYGBgDQoNCiMjIE1hcmtlciBHZW5lcyBmb3IgZWFjaCBjZWxsIHR5cGUNCg0KQmFzZWQgb24gV1QgdHJhbnNjcmlwdG9tZXMuDQoNCmBgYHtyfQ0KY2x1c3RlcmluZyA8LSAiQ2VsbFR5cGUxIg0KDQpJZGVudHMoYnlTYW1wbGUuY2NhKSA8LSBjbHVzdGVyaW5nDQoNCm1pblBjdCA8LSAzMA0KcHZhbCA8LSAwLjAxDQp1c2VBc3NheSA8LSAiUk5BIg0KbWV0aG9kIDwtICJ3aWxjb3giDQoNCkRlZmF1bHRBc3NheShieVNhbXBsZS5jY2EpIDwtIHVzZUFzc2F5DQpieVNhbXBsZS5jY2EuV1QgPC0gc3Vic2V0KGJ5U2FtcGxlLmNjYSwgU2FtcGxlTmFtZSA9PSAiQ0QxMWJfV1QiKQ0KYGBgDQoNCmBgYHtyIH0NCm1hcmtlcnMgPC0gYnlTYW1wbGUuY2NhLldUICU+JQ0KICBGaW5kQWxsTWFya2VycygNCiAgICBhc3NheSA9IHVzZUFzc2F5DQogICAgLCBzbG90ID0gImRhdGEiDQogICAgLCBtaW4ucGN0ID0gbWluUGN0LzEwMA0KICAgICwgcmV0dXJuLnRocmVzaCA9IHB2YWwNCiAgICAsIHRlc3QudXNlID0gbWV0aG9kDQogICAgLCB2ZXJib3NlID0gRg0KICAgICwgb25seS5wb3MgPSBUDQogICkNCmBgYA0KDQpgYGB7ciB9DQptYXJrZXJzICU+JQ0KICBmaWx0ZXIocF92YWxfYWRqIDwgcHZhbCkgJT4lDQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICBjb3VudCgpDQpgYGANCg0KIyMjIFRvcCAzMCBNYXJrZXJzIEhlYXRtYXANCg0KYGBge3IgZmlnLmFzcD0xLjR9DQpudG9wIDwtIDMwDQoNCnRvcE1hcmtlcnMgPC0gbWFya2VycyAlPiUgDQogIGZpbHRlcihwX3ZhbF9hZGogPCBwdmFsKSAlPiUNCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIHRvcF9uKG50b3AsIGF2Z19sb2cyRkMpDQoNCmJ5U2FtcGxlLmNjYSA8LSBieVNhbXBsZS5jY2EgJT4lDQogIFNjYWxlRGF0YShmZWF0dXJlcyA9IGMoVmFyaWFibGVGZWF0dXJlcyguKSx0b3BNYXJrZXJzJGdlbmUpLA0KICAgICAgICAgICAgYXNzYXkgPSB1c2VBc3NheSwNCiAgICAgICAgICAgIHZlcmJvc2UgPSBGKQ0KDQpieVNhbXBsZS5jY2EgJT4lDQogIERvSGVhdG1hcChhc3NheSA9IHVzZUFzc2F5LA0KICAgICAgICAgICAgZmVhdHVyZXMgPSB0b3BNYXJrZXJzJGdlbmUsDQogICAgICAgICAgICBncm91cC5jb2xvcnMgPSBjb2xvci5saXN0KQ0KYGBgDQoNCiMjIyBUb3AgOSBNYXJrZXJzIEV4cHJlc3Npb24NCg0KYGBge3IgZmlnLndpZHRoPTEyLGZpZy5hc3A9MC43fQ0KbnRvcCA8LSA5DQpmb3IgKG15Q2x1c3RlciBpbiBsZXZlbHMobWFya2VycyRjbHVzdGVyKSkgew0KICB0b3BNYXJrZXJzIDwtIG1hcmtlcnMgJT4lIA0KICBmaWx0ZXIocF92YWxfYWRqIDwgcHZhbCAmIGNsdXN0ZXIgPT0gbXlDbHVzdGVyKSAlPiUNCiAgdG9wX24obnRvcCwgYXZnX2xvZzJGQykNCiAgDQogIHByaW50KGJ5U2FtcGxlLmNjYSAlPiUNCiAgICBGZWF0dXJlUGxvdChmZWF0dXJlcyA9IHRvcE1hcmtlcnMkZ2VuZSwNCiAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidHNuZSIpICsNCiAgICAgIE5vTGVnZW5kKCkgKw0KICAgICAgcGxvdF9hbm5vdGF0aW9uKA0KICAgICAgICB0aXRsZSA9IHBhc3RlKCJNYXJrZXJzIGZvciBjZWxsIHR5cGU6IixteUNsdXN0ZXINCiAgICAgICAgICAgICAgICAgICAgICApDQopKQ0KfQ0KYGBgDQoNCiMjIyBUb3AgMTAgTWFya2VycyBEb3RQbG90DQoNCmBgYHtyIGZpZy53aWR0aD0xNH0NCm50b3AgPC0gMTANCnB2YWxjdXQgPC0gMC4wMQ0KDQp0b3BNYXJrZXJzIDwtIG1hcmtlcnMgJT4lDQogIGZpbHRlcihwX3ZhbF9hZGogPCBwdmFsKSAlPiUNCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIHRvcF9uKG50b3AsIGF2Z19sb2cyRkMpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGRwbHlyOjpzZWxlY3QoZ2VuZSkgJT4lDQogIGRpc3RpbmN0KCkNCg0KRG90UGxvdCgNCiAgYnlTYW1wbGUuY2NhLA0KICBhc3NheSA9IHVzZUFzc2F5LA0KICBncm91cC5ieSA9IGNsdXN0ZXJpbmcsDQogIGZlYXR1cmVzID0gcmV2KHRvcE1hcmtlcnMpDQogICkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LGhqdXN0ID0gMSkpDQpgYGANCg0KIyMgRGlmZmVyZW5jZXMgYmV0d2VlbiBDb25kaXRpb25zIGZvciBlYWNoIENlbGwgVHlwZQ0KDQpXaXRoaW4gZWFjaCBDZWxsIFR5cGUgd2UgaGF2ZSB0ZXN0ZWQgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gS08gYW5kIFdUIGNvbmRpdGlvbnMuDQoNCmBgYHtyfQ0KdGRlLmZpbGVzIDwtIE5VTEwNCmNvbnRyYXN0QnlDb25kIDwtIGxpc3QoKQ0KdXNlQXNzYXkgPC0gIlJOQSINCmNsdXN0ZXJpbmcgPC0gIkNlbGxUeXBlMSINCnRlc3RVc2UgPC0gIndpbGNveCINCm1pblBjdCA8LSAwLjENCg0KY29uZDEgPC0gIkNEMTFiX0tPIg0KY29uZDIgPC0gIkNEMTFiX1dUIg0KDQpEZWZhdWx0QXNzYXkoYnlTYW1wbGUuY2NhKSA8LSB1c2VBc3NheQ0KDQpieVNhbXBsZS5jY2EgPC0gYnlTYW1wbGUuY2NhICU+JQ0KICBTZXRJZGVudCh2YWx1ZSA9IHBhc3RlKGJ5U2FtcGxlLmNjYSRDZWxsVHlwZTEsYnlTYW1wbGUuY2NhJFNhbXBsZU5hbWUsc2VwPSIuIikpDQoNCmRlR2VuZXMgPC0gbGlzdCgpDQpmb3IgKG15Q2x1c3RlciBpbiBsZXZlbHMoZmFjdG9yKGJ5U2FtcGxlLmNjYSRDZWxsVHlwZTEpKSkgew0KICAgICAgICANCiAgICAgICAgaWRlbnQxIDwtIHBhc3RlKG15Q2x1c3Rlcixjb25kMSxzZXA9Ii4iKQ0KICAgICAgICBpZGVudDIgPC0gcGFzdGUobXlDbHVzdGVyLGNvbmQyLHNlcD0iLiIpDQogICAgICAgIA0KICAgICAgICBkZUdlbmVzW1tteUNsdXN0ZXJdXSA8LSBieVNhbXBsZS5jY2EgJT4lDQogICAgICAgICAgRmluZE1hcmtlcnMoDQogICAgICAgICAgICBhc3NheSA9IHVzZUFzc2F5DQogICAgICAgICAgICAsIHNsb3QgPSAiZGF0YSINCiAgICAgICAgICAgICwgaWRlbnQuMSA9IGlkZW50MQ0KICAgICAgICAgICAgLCBpZGVudC4yID0gaWRlbnQyDQogICAgICAgICAgICAsIG1pbi5wY3QgPSBtaW5QY3QNCiAgICAgICAgICAgICwgdGVzdC51c2UgPSB0ZXN0VXNlDQogICAgICAgICAgICAsIHZlcmJvc2UgPSBGDQogICAgICAgICAgKSAlPiUNCiAgICAgICAgICBtdXRhdGUoZ2VuZSA9IHJvd25hbWVzKC4pLA0KICAgICAgICAgICAgICAgICBjbHVzdGVyID0gbXlDbHVzdGVyKQ0KfQ0KDQpJZGVudHMoYnlTYW1wbGUuY2NhKSA8LSBjbHVzdGVyaW5nDQpgYGANCg0KYGBge3J9DQpwdmFsIDwtIDAuMDENCg0KZGVHZW5lcyA8LSBkby5jYWxsKHJiaW5kLGRlR2VuZXMpDQoNCmRlR2VuZXMgJT4lDQogIGZpbHRlcihwX3ZhbF9hZGogPCBwdmFsKSAlPiUNCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIGNvdW50KCkNCmBgYA0KDQojIyMgVG9wIDEwIERpZmZlcmVuY2VzIERvdFBsb3QNCg0KYGBge3IgZmlnLndpZHRoPTE0fQ0KbnRvcCA8LSAxMA0KcHZhbGN1dCA8LSAwLjAxDQoNCnRvcERlR2VuZXMgPC0gZGVHZW5lcyAlPiUNCiAgZmlsdGVyKHBfdmFsX2FkaiA8IHB2YWwpICU+JQ0KICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgdG9wX24obnRvcCwgYXZnX2xvZzJGQykgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgZHBseXI6OnNlbGVjdChnZW5lKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQpEb3RQbG90KA0KICBieVNhbXBsZS5jY2EsDQogIGFzc2F5ID0gIlJOQSIsDQogIGdyb3VwLmJ5ID0gY2x1c3RlcmluZywNCiAgZmVhdHVyZXMgPSByZXYodG9wRGVHZW5lcyksDQogIHNwbGl0LmJ5ID0gIlNhbXBsZU5hbWUiDQogICkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LGhqdXN0ID0gMSkpDQpgYGANCg0KIyMgSWwxQiBhbmQgSWwxcm4gRXhwcmVzc2lvbg0KDQpgYGB7cn0NCmRmIDwtIEVtYmVkZGluZ3MoYnlTYW1wbGUuY2NhLHJlZHVjdGlvbiA9ICJ0c25lIikNCmRuYW1lcyA8LSBjb2xuYW1lcyhkZikNCmRmIDwtIGNiaW5kKGRmLGJ5U2FtcGxlLmNjYUBtZXRhLmRhdGEpDQpkZiA8LSBjYmluZChkZixGZXRjaERhdGEoYnlTYW1wbGUuY2NhLHZhcnMgPSBjKCJJbDFiIiwiSWwxcm4iKSkpDQpgYGANCg0KYGBge3J9DQpkZiAlPiUNCiAgYXJyYW5nZShJbDFiKSAlPiUNCiAgZ2dwbG90KGFlc19zdHJpbmcoeD1kbmFtZXNbMV0seT1kbmFtZXNbMl0pKSArDQogIGdlb21fcG9pbnQoYWVzX3N0cmluZyhjb2xvcj0iSWwxYiIpKSArDQogIGZhY2V0X3dyYXAoIlNhbXBsZU5hbWUiKSArDQogIGdndGl0bGUoIklsMWIiKSArDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBjb2xvclJhbXBQYWxldHRlKGMoImdyZXkiLCJvcmFuZ2UiLCJyZWQiKSkoMyksbmFtZT0iTG9nKE5vcm1Db3VudHMpIikgKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQpgYGB7cn0NCmRmICU+JQ0KICBhcnJhbmdlKElsMXJuKSAlPiUNCiAgZ2dwbG90KGFlc19zdHJpbmcoeD1kbmFtZXNbMV0seT1kbmFtZXNbMl0pKSArDQogIGdlb21fcG9pbnQoYWVzX3N0cmluZyhjb2xvcj0iSWwxcm4iKSkgKw0KICBmYWNldF93cmFwKCJTYW1wbGVOYW1lIikgKw0KICBnZ3RpdGxlKCJJbDFybiIpICsNCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZ3JleSIsIm9yYW5nZSIsInJlZCIpKSgzKSxuYW1lPSJMb2coTm9ybUNvdW50cykiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCmBgYHtyIGZpZy53aWR0aD0xNCwgZmlnLmFzcD0wLjV9DQpkZiAlPiUNCiAgbXV0YXRlKElsMUV4cCA9IGNhc2Vfd2hlbigNCiAgICBJbDFiPjAgJiBJbDFybiA9PSAwIH4gIklsMWIgb25seSIsDQogICAgSWwxYj09MCAmIElsMXJuID4gMCB+ICJJbDFybiBvbmx5IiwNCiAgICBJbDFiPjAgJiBJbDFybiA+IDAgfiAiSWwxYiBhbmQgSWwxcm4iKSkgJT4lDQogZ2dwbG90KGFlcyh4PSIiLGZpbGw9SWwxRXhwKSkgKw0KICBnZW9tX2JhcihzdGF0PSJjb3VudCIscG9zaXRpb249ImZpbGwiKSArDQogIGNvb3JkX3BvbGFyKCJ5IixzdGFydCA9IDApICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3IubGlzdCkgKw0KICBmYWNldF93cmFwKGMoIlNhbXBsZU5hbWUiLCJDZWxsVHlwZTEiKSxuY29sID0gNikgKw0KICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQojIyBJbDFiIHZzIElsMXJuDQoNCmBgYHtyfQ0KcCA8LSBkZiAlPiUNCiAgZmlsdGVyKFNhbXBsZU5hbWUgPT0gIkNEMTFiX1dUIikgICU+JQ0KICBtdXRhdGUoSWwxRXhwID0gY2FzZV93aGVuKA0KICAgIElsMWI+MCAmIElsMXJuID09IDAgfiAiSWwxYiBvbmx5IiwNCiAgICBJbDFiPT0wICYgSWwxcm4gPiAwIH4gIklsMXJuIG9ubHkiLA0KICAgIElsMWI+MCAmIElsMXJuID4gMCB+ICJJbDFiIGFuZCBJbDFybiIpKSAlPiUNCiAgZ2dwbG90KGFlcyh4PUlsMXJuLHk9SWwxYikpICsNCiAgZ2d0aXRsZSgiTXllbG9pZCBXVCBDZWxscyIpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCmdnTWFyZ2luYWwocCx0eXBlID0gImRlbnNpZ3JhbSIpDQpgYGANCg0KDQojIyBWaW9saW4gcGxvdHMgb24gU2VsZWN0ZWQgR2VuZXMNCg0KYGBge3J9DQpzZWxlY3RlZEdlbmVzIDwtIGMoIkx5biIsIkhpZjFhIiwiTG1vNCIsIkNzZjJyYiIsIk15ZDg4IiwiQ3hjcjIiLCJOZmtiaWEiLCJDZWJwYiIpDQpgYGANCg0KYGBge3J9DQpkZiA8LSBGZXRjaERhdGEoYnlTYW1wbGUuY2NhLHZhcnMgPSBzZWxlY3RlZEdlbmVzKQ0KZGYgPC0gY2JpbmQoZGYsYnlTYW1wbGUuY2NhQG1ldGEuZGF0YSkNCmRmIDwtIGRmICU+JSBtdXRhdGUoU2FtcGxlTmFtZT1mYWN0b3IoU2FtcGxlTmFtZSxsZXZlbHMgPSBjKCJDRDExYl9XVCIsIkNEMTFiX0tPIikpKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShzdHJpbmdyKQ0KcExpc3QgPC0gbGlzdCgpDQpmb3IgKGdlbmUgaW4gc2VsZWN0ZWRHZW5lcykgew0KICBwTGlzdFtbbGVuZ3RoKHBMaXN0KSsxXV0gPC0gZ2dwbG90KGRmLGFlc19zdHJpbmcoeT1nZW5lLHg9IlNhbXBsZU5hbWUiLGNvbG9yPSJTYW1wbGVOYW1lIixmaWxsPSJTYW1wbGVOYW1lIikpICsNCiAgICBnZW9tX3Zpb2xpbihzdGF0ID0gInlkZW5zaXR5IixhbHBoYT0wLjIpICsNCiAgICAjIGdlb21faml0dGVyKGFscGhhPTEpICsNCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZ3JleTQwIiwiI2E4MGEwYiIpKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiZ3JleTQwIiwiI2E4MGEwYiIpKSArDQogICAgc3RhdF9zdW1tYXJ5KGZ1biA9ICJtZWFuIiwNCiAgICAgICAgICAgICAgIGdlb20gPSAiY3Jvc3NiYXIiLCANCiAgICAgICAgICAgICAgIHdpZHRoID0gMC41LA0KICAgICAgICAgICAgICAgY29sb3VyID0gImJsYWNrIikgKw0KICAgIHlsYWIoIkV4cHJlc3Npb24gTGV2ZWwgbG9nKE5vcm1Db3VudHMrMSkiKSArDQogICAgZ2d0aXRsZShzdHJfdG9fdGl0bGUoZ2VuZSkpICsNCiAgICBmYWNldF93cmFwKCJDZWxsVHlwZTEiLG5yb3cgPSAxKSArDQogICAgdGhlbWVfY2xhc3NpYygpICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSxsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQp9DQoNCnAgPC0gbGFwcGx5KHBMaXN0LHBsb3QpDQpgYGANCg0KIyBTZXNzaW9uIEluZm8NCg0KYGBge3J9DQpzZXNzaW9uSW5mbygpDQpgYGANCg0KDQo=